diff --git a/Makefile b/Makefile index 66ea068..265a7c4 100644 --- a/Makefile +++ b/Makefile @@ -9,6 +9,7 @@ utilities: $(AS) -o bin/chmod src/cmd/chmod.s $(AS) -o bin/chown src/cmd/chown.s $(AS) -o bin/chrm src/cmd/chrm.s + $(AS) -o bin/ls src/other/wktls.s clean: rm -f bin/* diff --git a/README.md b/README.md index 89ad703..0116287 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,19 @@ # pdp7-unix -This is a project to resurrect Unix on the PDP-7 from a scan of the original -assembly code done by Norman Wilson. - -Right now the scans are in the Unix Archive at +This is a project to resurrect Unix on the PDP-7 from scans of the original assembly +code done by Norman Wilson. The scans of PDP-7 Unix are in the Unix Archive at http://www.tuhs.org/Archive/PDP-11/Distributions/research/McIlroy_v0/ -as the files 0*.pdf. We now need to convert these to machine-readable -assembly code, write tools such as an assembler, a filesystem creation tool, -and write from scratch missing things like a shell, ls etc. +as the files 0*.pdf. + +Update early March 2016: We've written an assembler, most of a user-mode +simulator and commented several source files. We now have these utilities +running: cat, cp, chmod, chown, chrm and ls. + +Things to do: write a filesystem creation tool, write a shell, write the +missing utilities, try to bring the system up on a PDP-7 system. We have +a real PDP-7 and SimH as target platforms. The code in the original scans are (c) Novell who own the rights to the Unix -source code. Everything that didn't come from the scanned pages are GPLv3. +source code. Everything that didn't come from the scanned files is GPLv3. scans holds the unmodified OCR versions of the scanned files @@ -17,6 +21,8 @@ src/cmd holds the modified source code of the user-mode programs src/sys holds the modified source code of the kernel +src/other holds PDP-7 source code which did not come from the scanned files + tools holds the source for the tools written to assist the project misc holds miscellaneous notes and information diff --git a/scans/bi.s b/scans/bi.s index eb4dd2a..f8abd9d 100644 --- a/scans/bi.s +++ b/scans/bi.s @@ -247,11 +247,11 @@ call: isz 8 -1 tad sp - sad B + sad 8 skp jmp 1b lac ap i - img + lmq lac dp dac ap i lac ap diff --git a/src/cmd/adm.s b/src/cmd/adm.s index f747395..c78e67b 100644 --- a/src/cmd/adm.s +++ b/src/cmd/adm.s @@ -54,7 +54,7 @@ floop1: tad d4 dac name - sys open; name; ..; 0 + sys open; name: ..; 0 spa jmp ferror dac fi @@ -189,7 +189,7 @@ ferror: sys write; 1:..; 4 lac d1 sys write; 1f; 1 - fmp floop1 + jmp floop1 1: 077012 hangup: diff --git a/src/cmd/apr.s b/src/cmd/apr.s index 464422b..255c003 100644 --- a/src/cmd/apr.s +++ b/src/cmd/apr.s @@ -62,7 +62,7 @@ floop1: tad d4 dac name - sys open; name; ..; 0 + sys open; name: ..; 0 spa jmp ferror dac fi @@ -72,7 +72,7 @@ ferror: lac name dac 1f lac d1 - sys write; 1;..; 4 + sys write; 1:..; 4 lac d1 sys write; 1f; 1 jmp floop1 @@ -134,7 +134,7 @@ bksp: jmp cloop cret: - czm col + dzm col jmp cloop pass2: @@ -278,6 +278,7 @@ hangup: abort: lac d1 sys write; m2; m2s + jmp stop nofiles: lac d1 @@ -342,7 +343,7 @@ gcard: 0 isz gcard sna jmp gcard i - irss 9 + lrss 9 sad o45 jmp 1f jms putc diff --git a/src/cmd/bc.s b/src/cmd/bc.s index b176f2b..b857335 100644 --- a/src/cmd/bc.s +++ b/src/cmd/bc.s @@ -59,7 +59,7 @@ trace: 0 jmp stop jmp trace i -itrace: 0 +dtrace: 0 lac pc dac 8 lac 8 i diff --git a/src/cmd/bi.s b/src/cmd/bi.s index 322163f..f8abd9d 100644 --- a/src/cmd/bi.s +++ b/src/cmd/bi.s @@ -8,7 +8,7 @@ start: fetch: lac pc i - imq + lmq and o17777 dac addr ecla lls 4 @@ -79,7 +79,7 @@ bor: jmp fetch band: - lac t2 i + lac t1 i and t2 i dac t4 i jmp fetch @@ -185,7 +185,7 @@ consop: tad d1 dac sp i isz sp - iac addr + lac addr dac sp i isz sp jmp fetch @@ -206,13 +206,13 @@ mcall: dac t2 -1 tad t2 i - imq + lmq lac dp dac t1 i lac t1 dac dp isz t1 - iac pc + lac pc dac t1 i lacq dac pc @@ -247,11 +247,11 @@ call: isz 8 -1 tad sp - sad B + sad 8 skp jmp 1b lac ap i - img + lmq lac dp dac ap i lac ap @@ -364,24 +364,24 @@ unaop: jmp . i uadr; umin; uind; unot -badr: +uadr: lac t1 dac t3 i jmp fetch -bmin: +umin: -1 tad t1 i cma dac t3 i jmp fetch -bind: +uind: lac t1 i dac t2 i jmp fetch -bnot: +unot: lac t1 i sna cla lac d1 @@ -412,22 +412,22 @@ s = n+a t = s+a u = t+a x = u+a -f = x+a +y = x+a d1: 1 dm1: -1 dm2: -2 o17777: 017777 -:1: 0 -:2: 0 -:3: 0 -:4: 0 -:ddr: 0 +t1: 0 +t2: 0 +t3: 0 +t4: 0 +addr: 0 pc = 017 sp: stack dp: stack -ip: stack +ap: stack stack: 0 \ No newline at end of file diff --git a/src/cmd/ds.s b/src/cmd/ds.s index 520ed80..d26d0e4 100644 --- a/src/cmd/ds.s +++ b/src/cmd/ds.s @@ -2,7 +2,7 @@ lac 017777 i sad d8 - skp + skp sys exit lac 017777 tad d5 @@ -12,13 +12,16 @@ dac fo law 017 sys creat; scrname - spa; jms error + spa + jms error dac fso sys open; scrname; 0 - spa; jms error + spa + jms error dac fsi sys chdir; dd - spa; jms error + spa + jms error lac d1 sys write; pass1; 1 law fsobuf @@ -31,12 +34,14 @@ dac dirp dzm fsloc sys open; dotdot; 0 - spa; jms error + spa + jms error dac fd jms readdir; dotdot law statbuf sys status; dotdot; dotdot - spa; jms error + spa + jms error lac statbuf+12 " i index dac dirp i isz dirp @@ -75,13 +80,15 @@ loop: sys read; scrname; 4 law statbuf sys status; dotdot; scrname - spa; jms error + spa + jms error lac statbuf+0 " flags and o20 sna - jmp 2f + jmp 2f sys open; scrname; 0 - spa; jms error + spa + jms error dac fd jms readdir; scrname lac ddfilp i @@ -109,7 +116,7 @@ loop: dzm fflg law fbuf dac i2 - r1 + -1 tad nfiles cma dac c2 @@ -118,19 +125,19 @@ loop: lac c1 tad d501 sad i2 i - skp + skp jmp 3f -1 tad i1 dac i3 - iac i3 i + lac i3 i dac c3 law fbuf dac i3 0: lac i3 i sad c3 - jmp 0f + jmp 0f isz i3 isz i3 jmp 0b @@ -159,14 +166,15 @@ loop: lac nlinkt sad nlinka - skp + skp jms fishy dzm nlinka law 012 jms putc law statbuf sys status; scrname; dd - spa; jms error + spa + jms error -1 tad statbuf+9 cma @@ -188,7 +196,7 @@ loop: isz i2 lac i2 sad i1 i - skp + skp jmp .+3 isz i1 isz i1 @@ -199,7 +207,7 @@ loop: jmp 1b lac nlinkt sad nlinka - skp + skp jms fishy sys chdir; system @@ -267,9 +275,10 @@ readdir: 0 jms copyz; buf; 64 lac fd sys read; buf; 64 - spa; jms error + spa + jms error sna - jmp 4f + jmp 4f -8 dac c1 law buf @@ -277,7 +286,7 @@ readdir: 0 1: lac i1 i sna - jmp 3f + jmp 3f isz nfiles dac filp i @@ -292,7 +301,8 @@ readdir: 0 dac .+4 law statbuf sys status; 5:..; .. - spa; jms error + spa + jms error jms longout lac i1 tad d1 @@ -316,7 +326,7 @@ readdir: 0 isz fsopt law fsobuf+64 sad fsopt - skp + skp jmp 3f lac fso sys write; fsobuf; 64 @@ -365,7 +375,8 @@ octal: 0 lac octal i dac c 1: - ecla llss 3 + " ecla llss 3 + llss 3 tad o60 jms putc isz c @@ -401,10 +412,10 @@ copyz: 0 done: lac noc sna - sys exit + sys exit and d1 sna cla - jmp 1f + jmp 1f jms putc jmp done 1: diff --git a/src/other/wktls.s b/src/other/wktls.s new file mode 100644 index 0000000..f8dba88 --- /dev/null +++ b/src/other/wktls.s @@ -0,0 +1,59 @@ +" Warren's version of ls. Simply print out the names in the current directory + +main: + sys open; curdir; 0 " Open up the currect directory + spa + sys exit " Unable, so die now + dac fd " Save the fd + +fileloop: + " Read 64 words into the buffer from the input file + lac fd + sys read; buf; 64 + spa " Skip if result was >= 0 + jmp fileend " Result was -ve, so error result + sna " Skip if result was >0 + jmp fileend " Result was zero, so nothing left to read + + " Save the count of words read in + dac count + lac ibufptr " Point bufptr at the base of the buffer + dac bufptr + +" Each directory entry is eight words. We need to print out +" the filename which is in words 2 to 5. +printloop: + + isz bufptr " Move up to the filename + lac d1 + sys write; bufptr:0; 4 " Write a filename out to stdout + lac d1 + sys write; newline; 1 " followed by a newline + + lac bufptr " Add 7 to the bufptr + tad d7 + dac bufptr + -8 + tad count " Decrement the count of words by 8 + dac count + sza " Anything left in the buffer to print? + jmp printloop " Yes, stuff left to print + jmp fileloop " Nothing in the buffer, try reading some more + +fileend: + " Close the open file descriptor and exit + lac fd + sys close + sys exit + +curdir: <. 040; 040040; 040040; 040040 " i.e. "." +newline: 012000 + +fd: 0 +d1: 1 " stdout fd +d7: 7 +count: 0 + +" Input buffer for read +ibufptr: buf " Constant pointer to the buffer +buf: .=.+64 diff --git a/src/sys/s1.s b/src/sys/s1.s index 8dcf364..f50088c 100644 --- a/src/sys/s1.s +++ b/src/sys/s1.s @@ -3,22 +3,22 @@ .. = 0 t = 0 orig: - hlt - jmp pibreak + hlt " overwritten with interrupt return addr + jmp pibreak " dispatch to interrupt processing . = orig+7 - -1 + -1 " only ever set (to -1): never read?! -. = orig+020 - 1f - iof - dac u.ac - lac 020 - dac 1f +. = orig+020 " syscall (CAL) processing + 1f " addr for "CAL I": store return here on "CAL" + iof " interrupts off + dac u.ac " save user AC + lac 020 " save user return addr + dac 1f " save as if "CAL I" lac 1f-1 - dac 020 - lac u.ac - jmp 1f+1 + dac 020 " restore location 20 + lac u.ac " restore user AC + jmp 1f+1 " join "CAL I" processing 1f 1: 0 iof " interrupts off @@ -49,7 +49,7 @@ orig: jmp .. i " dispatch system call . = orig+0100 - jmp coldentry + jmp coldentry " here to start kernel jms halt okexit: @@ -64,9 +64,9 @@ sysexit: " common system call exit code jms dskio; 07000 " save to disk? 1: dzm .insys " clear "in system call" flag - jms chkint - skp - jmp .save " dump core?? + jms chkint " pending interrupt? + skp " no + jmp .save " yes: dump core jms copy; u.rq+2; 10; 6 " restore auto-index locations 10-15 lac u.rq+1 " restore auto-index location 9 dac 9 @@ -77,6 +77,7 @@ sysexit: " common system call exit code lac u.ac " restore AC register jmp u.rq+8 i " return to user + " scheduler / idle loop swap: 0 ion 1: @@ -84,49 +85,49 @@ swap: 0 jmp 1f jms lookfor; 1 " in/ready skp - jmp 1b - dzm maxquant + jmp 1b " loop until a process becomes ready + dzm maxquant " here with in/ready (self?) jmp 3f -1: - dac 9f+t - jms lookfor; 2 " in/notready +1: " here with out/ready process + dac 9f+t " save process pointer (swapped out) in t0 + jms lookfor; 2 " in/notready " find a swapped in process to swap out? jmp 1f jms lookfor; 1 " in/ready jmp 1f jmp 2f 1: lac swap - dac u.swapret + dac u.swapret " return to scheduler when swapped back iof - lac o200000 + lac o200000 " change status to swapped out tad u.ulistp i dac u.ulistp i ion - jms dskswap; 07000 + jms dskswap; 07000 " swap process out lac u.dspbuf sna jmp 2f law dspbuf jms movdsp 2: - iof - lac o600000 + iof " disable interrupts + lac o600000 " change status (1->7?) tad 9f+t i dac 9f+t i - ion - jms dskswap; 06000 - lac u.swapret - dac swap - lac o20 + ion " enable interrupts + jms dskswap; 06000 " read process in? + lac u.swapret " set our return addr + dac swap " to saved return addr + lac o20 " reset maxquant to 16 ticks dac maxquant lac u.dspbuf - sza + sza " using display? "** 01-s1.pdf page 4 - jms movdsp + jms movdsp " yes. 3: - dzm uquant + dzm uquant " no. reset process tick count iof - jmp swap i + jmp swap i " return t = t+1 swp: " system call dispatch table @@ -138,12 +139,18 @@ swp: " system call dispatch table swn: .-swp-1 i " count of system calls, plus indirect! + " AC/ new value for intflg (non-zero to ignore interrupt char) + " sys intrp .intrp: lac u.ac dac u.intflg jmp okexit -.sysloc: " "sysloc": syscall to return system addresses + " syscall to retrieve system addresses (data & routines!!) + " AC/ index (1..17) + " sys sysloc + " AC/ address (or -1 on bad index) +.sysloc: lac u.ac and o17777 jms betwen; d1; locn @@ -154,7 +161,7 @@ swn: dac u.ac jmp sysexit -locsw: " table of system data structures for "sysloc" call +locsw: " table of system addresses for sysloc lac . iget; inode; userdata; sysdata; copy; copyz; betwen; dskrd dskwr; dskbuf; dpdata; namei; pbsflgs; alloc; free; dspdata @@ -162,34 +169,41 @@ locsw: " table of system data structures for "sysloc" call locn: .-locsw-1 + " check if interrupt for user + " checks .int1 and .int2 (contain i-number of interrupt source) + " call: + " .insys/ 0 + " jms chkint + " no: no interrupt, or intflg set (discards interupt) + " yes: PI off, .insys set chkint: 0 lac .insys - sza - jmp chkint i - lac .int1 - sna - jmp 1f - sad u.ofiles+2 - jmp 2f + sza " in system? + jmp chkint i " yes: return + lac .int1 " get inumber of interrupt1 source? + sna " zero? + jmp 1f " yes: skip stdin check + sad u.ofiles+2 " non-zero: compare to stdin inumber + jmp 2f " same 1: - lac .int2 - sna - jmp chkint i - sad u.ofiles+2 - skp - jmp chkint i - dzm .int2 + lac .int2 " get inum of interrupt 2 source? + sna " zero? + jmp chkint i " yes: return + sad u.ofiles+2 " non-zero: compare to stdin inumber + skp " match! + jmp chkint i " no match: return + dzm .int2 " clear int2 source jmp 1f 2: - dzm .int1 + dzm .int1 " clear int1 source 1: "** 01-s1.pdf page 5 - lac u.intflg - sza - jmp chkint i + lac u.intflg " get user intflg + sza " zero? + jmp chkint i " no: ignore -1 - dac .insys - ion - isz chkint + dac .insys " set "in system" flag + ion " enable interrupts + isz chkint " give skip return jmp chkint i diff --git a/src/sys/s2.s b/src/sys/s2.s index 2119ec5..4006ed5 100644 --- a/src/sys/s2.s +++ b/src/sys/s2.s @@ -190,36 +190,39 @@ "** 01-s1.pdf page 10 jmp okexit + " open system call + " sys open; filename_ptr; flags (0 for read, 1 for write) + " returns w/ "fd" in AC (or -1 if not found) .open: - jms arg - dac 0f - jms arg - sza - lac d1 - sna - lac d2 " mode bit 2 (read?) + jms arg " get filename + dac 0f " save for namei + jms arg " get flags + sza " zero (read) + lac d1 " no: get write mode bit + sna " non-zero (write)? + lac d2 " no: get read mode bot dac mode " save for access call - lac u.cdir - jms namei; 0:0 - jms error - jms iget - jms access - lac i.flags - and o20 - sna - jmp open1 - lac mode - and d1 - sna - jmp open1 - lac u.uid - sma - jms error - jmp open1 + lac u.cdir " get current working directory + jms namei; 0:0 " search for file + jms error " error: return -1 + jms iget " load inode + jms access " check access (may return w/ error to user) + lac i.flags " get file flags + and o20 " get directory bit + sna " is directory? + jmp open1 " no, join common code + lac mode " get access mode + and d1 " get write bit + sna " write access? + jmp open1 " no, continue + lac u.uid " yes: get uid? + sma " negative? (-1 is superuser) + jms error " no: return error + jmp open1 " yes: join common code .creat: - lac d1 " mode bit 1 (write?) - dac mode " save for access call + lac d1 " mode bit 1 (write) + dac mode " save for access call jms arg dac .+2 jms copy; ..; name; 4 @@ -241,12 +244,12 @@ jmp open1 1: jms access - lac u.ac - and o17 + lac u.ac " get access bits from user AC (zero for lock!) + and o17 " mask to permissions jms icreat -open1: - jms fassign - jms error +open1: " common exit for open/creat + jms fassign " assign fd slot + jms error " none free, return -1 jmp sysexit "** 01-s1.pdf page 11 @@ -275,63 +278,67 @@ open1: and d1 sza jms error - lac i.flags - and o40 - sna - jmp 1f - iof - lac ii - tad swr + lac i.flags " get inode flags + and o40 " get special file bit + sna " special? + jmp 1f " no + iof " yes: disable interrupts + lac ii " get i number + tad swr " get read routine table addr dac .+1 - jmp .. i + jmp .. i " dispatch to read routine 1: - lac u.base - dac 1f+1 - lac u.count - dac 1f+2 - lac f.badd + lac u.base " get user base + dac 1f+1 " save as iread base + lac u.count " get user count + dac 1f+2 " save as iread count + lac f.badd " get file offset 1: jms iread; ..; .. jmp exitrw + " write system call: + " AC/ fd + " sys write; buffer; count + " AC/ count or -1 on error .write: - jms arg - and o17777 - dac u.base - jms arg - dac u.count - tad u.base - jms betwen; u.base; o17777 - jms error - dac u.limit - jms finac - lac f.flags - and d1 - sna - jms error - lac i.flags - and o40 + jms arg " pick up buffer + and o17777 " mask to addr + dac u.base " save as I/O base + jms arg " pick up count + dac u.count " save as count + tad u.base " add base (get limit) + jms betwen; u.base; o17777 " check between base and end of memory + jms error " no: error + dac u.limit " yes: save as limit + jms finac " get fnode with fd from user AC + lac f.flags " get open file table flags + and d1 " open for write? + sna " yes, skip + jms error " no: error + lac i.flags " get inode flags + and o40 " get special bit? "** 01-s1.pdf page 12 - sna - jmp 1f - iof - lac ii - tad sww + sna " special? + jmp 1f " no + iof " special file (device node) + lac ii " get i number + tad sww " get write routine dac .+1 - jmp .. i + jmp .. i " dispatch to write routine +1: " here with regular file + lac u.base " get base + dac 1f+1 " save as iwrite arg 1 + lac u.count " get count + dac 1f+2 " save as iwrite 2 + lac f.badd " get fd offset 1: - lac u.base - dac 1f+1 - lac u.count - dac 1f+2 - lac f.badd -1: - jms iwrite; ..; .. + jms iwrite; ..; .. " write to file -exitrw: - dac u.ac +exitrw: " common exit for read/write system calls + dac u.ac " save return in user AC tad f.badd - dac f.badd - jms iput - jms fput - jmp sysexit + dac f.badd " update file offset + jms iput " release inode + jms fput " release fnode + jmp sysexit " return to user diff --git a/src/sys/s3.s b/src/sys/s3.s index c0ca18d..2947c11 100644 --- a/src/sys/s3.s +++ b/src/sys/s3.s @@ -1,15 +1,21 @@ "** 01-s1.pdf page 14 " s3 + " search for user (process) + " call: + " jms searchu; worker_routine_addr + " worker called with copy of a process table entry in "lu" + " can return directly (from caller of searchu) + " index location 8 points to next process table entry searchu: 0 - lac searchu i - dac 9f+t+1 - -mnproc - dac 9f+t - law ulist-1 - dac 8 + lac searchu i " fetch argument + dac 9f+t+1 " in t1 + -mnproc " loop counter + dac 9f+t " in t0 + law ulist-1 " ulist ptr + dac 8 " in index 8 1: - lac 8 i + lac 8 i " copy ulist entry to lu dac lu lac 8 i dac lu+1 @@ -17,86 +23,96 @@ searchu: 0 dac lu+2 lac 8 i dac lu+3 - jms 9f+t+1 i - isz 9f+t - jmp 1b - isz searchu + jms 9f+t+1 i " call argument as subroutine + isz 9f+t " returned: loop done? + jmp 1b " no, do it again + isz searchu " skip argument jmp searchu i t = t+2 + " look for a process with matching status + " jms lookfor; status + " found: ulist ptr in AC + " not found lookfor: 0 jms searchu; 1f - isz lookfor - isz lookfor + isz lookfor " skip argument + isz lookfor " give skip return jmp lookfor i -1: 0 +1: 0 " worker called by searchu lac lu - rtl; rtl; and o7 - sad lookfor i - skp - jmp 1b i + rtl; rtl; and o7 " bits 0:2 of lu + sad lookfor i " match argument? + skp " yes + jmp 1b i " no, return, keep going -3 - tad 8 + tad 8 " roll index 8 back to this entry and o17777 - isz lookfor - jmp lookfor i + isz lookfor " skip lookfor argument + jmp lookfor i " non-skip return + " fork system call: + " sys fork + " return at +1 in parent, child pid in AC + " return at +2 in child, parent pid in AC .fork: - jms lookfor; 0 " not-used + jms lookfor; 0 " not-used " find an unused process slot skp - jms error - dac 9f+t - isz uniqpid + jms error " none found- return error + dac 9f+t " save ulist ptr in t0 + isz uniqpid " generate new pid lac uniqpid - dac u.ac + dac u.ac " return in child pid in AC law sysexit - dac u.swapret - lac o200000 + dac u.swapret " return from system call when swapped back in + lac o200000 " change process status to out/ready (1->3) tad u.ulistp i dac u.ulistp i - jms dskswap; 07000 - lac 9f+t - dac u.ulistp - lac o100000 + jms dskswap; 07000 " swap parent out + lac 9f+t " get unused ulist slot back + dac u.ulistp " set ulist pointer + lac o100000 " mark child in/notready? (3->2) xor u.ulistp i dac u.ulistp i - lac u.pid + lac u.pid " get old (parent) pid "** 01-s1.pdf page 15 - dac u.ac + dac u.ac " return parent pid in AC lac uniqpid - dac u.pid - isz 9f+t - dac 9f+t i - isz u.rq+8 - dzm u.intflg - jmp sysexit + dac u.pid " set child pid + isz 9f+t " advance to second word in process table + dac 9f+t i " set pid in process table + isz u.rq+8 " give skip return + dzm u.intflg " clear int flag + jmp sysexit " return in child process t= t+1 -badcal: - clon +badcal: " bad (unimplemented) system call + clon " clear any pending clock interrupt? -1 - dac 7 -.save: - lac d1 + dac 7 " set location 7 to -1?! + " fall into "save" system call + " Ken says save files could be resumed, and used for checkpointing! +.save: " "sys save" system call + lac d1 " get inode 1 (core file?) jms iget cla - jms iwrite; 4096; 4096 - jms iwrite; userdata; 64 + jms iwrite; 4096; 4096 " dump core + jms iwrite; userdata; 64 " and user area jms iput .exit: lac u.dspbuf - sna - jmp .+3 - law dspbuf - jms movdsp + sna " process using display? + jmp .+3 " no + law dspbuf " yes + jms movdsp " move display jms awake lac u.ulistp i - and o77777 + and o77777 " mark process table entry free dac u.ulistp i isz u.ulistp - dzm u.ulistp i - jms swap + dzm u.ulistp i " clear pid in process table + jms swap " find a new process to run .rmes: jms awake @@ -124,25 +140,28 @@ badcal: t = t+1 "** 01-s1.pdf page 16 + " smes system call + " AC/ pid + " sys smes .smes: - lac u.ac - sna spa - jms error - jms searchu; 1f + lac u.ac " get pid from user AC + sna spa " >0? + jms error " no: error + jms searchu; 1f " search for process law 2 tad u.ulistp dac 9f+t dzm 9f+t i jms error -1: 0 - lac lu+1 - sad u.ac - skp - jmp 1b i - lac lu+2 - sad dm1 - jmp 1f - lac o100000 +1: 0 " worker for searchu + lac lu+1 " get pid + sad u.ac " match? + skp " yes + jmp 1b i " no + lac lu+2 " get mailbox + sad dm1 " -1? + jmp 1f " yes + lac o100000 " no: increment process status tad u.ulistp i dac u.ulistp i law 2 @@ -177,19 +196,19 @@ t = t+1 awake: 0 jms searchu; 1f jmp awake i -1: 0 - lac u.pid - sad lu+2 - skp - jmp 1b i +1: 0 " searchu worker + lac u.pid " get caller pid + sad lu+2 " match process table entry? + skp " yes + jmp 1b i " no, return -3 - tad 8 - dac 9f+t + tad 8 " get pointer to pid in process table?? + dac 9f+t " save in t0 "** 01-s1.pdf page 17 - lac o700000 + lac o700000 " set high bits tad 9f+t i dac 9f+t i - jmp 1b i + jmp 1b i " return from worker t = t+1 swr: @@ -200,15 +219,16 @@ sww: .halt: jms halt + " read routine for ttyin special file rttyi: jms chkint1 lac d1 jms getchar jmp 1f and o177 - jms betwen; o101; o132 - skp - tad o40 + jms betwen; o101; o132 " upper case? + skp " no + tad o40 " yes: convert to lower alss 9 jmp passone 1: @@ -216,6 +236,7 @@ rttyi: jms swap jmp rttyi + " write routine for ttyout special file wttyo: jms chkint1 jms forall @@ -243,6 +264,7 @@ wttyo: jms swap jmp wttyo + " read routine for (display) "keyboard" special file rkbdi: jms chkint1 lac d3 @@ -282,6 +304,7 @@ rkbdi: jms swap jmp rkbdi + " write routine for (graphic) "display" special file wdspo: jms chkint1 jms forall @@ -292,6 +315,7 @@ wdspo: jmp wdspo + " read routine for paper tape reader special file rppti: lac d4 jms getchar @@ -307,6 +331,7 @@ rppti: jmp rppti "** 01-s1.pdf page 19 + " write routine for paper tape punch special file wppto: jms forall sna @@ -331,6 +356,7 @@ wppto: jms swap jmp wppto + " common exit for special file passone: sad o4000 jmp okexit diff --git a/src/sys/s4.s b/src/sys/s4.s index 51c2db3..5d37eec 100644 --- a/src/sys/s4.s +++ b/src/sys/s4.s @@ -1,7 +1,7 @@ "** 01-s1.pdf page 21 " s4 - " allocate a free disk block + " allocate a free disk block for a file (data or indirect) alloc: 0 -1 tad s.nfblks @@ -92,11 +92,14 @@ betwen: 0 cma " complement (AC-high) spa sna " AC-high <= 0? 1: - isz betwen " no: give happy return + isz betwen " no: give happy (skip) return lacq " restore ~AC cma " restore AC jmp betwen i " return w/o skip + " copy memory + " call: + " jms copy; src; dest; count copy: 0 -1 tad copy i @@ -118,21 +121,24 @@ copy: 0 jmp 1b jmp copy i + " copy zeroes (clear memory) + " call: + " jms copyz; pointer; count copyz: 0 -1 - tad copyz i - dac 8 - isz copyz + tad copyz i " get call PC + dac 8 " save in index (pre-increments) + isz copyz " skip pointer -1 - tad copyz i - cma - dac 9f+t - isz copyz + tad copyz i " get count-1 + cma " get negative count + dac 9f+t " save in t0 + isz copyz " skip count 1: - dzm 8 i - isz 9f+t - jmp 1b - jmp copyz i + dzm 8 i " zero word + isz 9f+t " done? + jmp 1b " no: loop + jmp copyz i " return t = t+1 putchar: 0 @@ -281,6 +287,8 @@ dskrd: 0 jms collapse jmp dskrd i + " write a file block (data, inode or indirect) + " AC/ block dskwr: 0 jms betwen; d2; d7999 jms halt @@ -292,7 +300,6 @@ dskwr: 0 jmp dskwr i t = t+3 - " called with: " AC/ block " jms dskio; dsld_bits dskio: 0 @@ -354,11 +361,11 @@ dsktrans: 0 t = t+1 halt: 0 - isz 9f+t + isz 9f+t " spin for a while (process interrupts) jmp .-1 - iof - hlt - jms copy; law; 4096; 4096 - hlt; jmp .-1 + iof " disable interrupts + hlt " halt + jms copy; law; 4096; 4096 " continued: copy system up to user memory? + hlt; jmp .-1 " halt for good t = t+1 diff --git a/src/sys/s5.s b/src/sys/s5.s index 4965236..f83f6e8 100644 --- a/src/sys/s5.s +++ b/src/sys/s5.s @@ -1,75 +1,86 @@ "** 01-s1.pdf page 28 " s5 - + " read/write a process from/to swap space + " call: + " AC/ first word of process table + " jms dskswap; DSLD bits dskswap: 0 - cll; als 3 - dac 9f+t - jms dsktrans; -64; userdata; 9f+t; dskswap - lac 9f+t - tad o20 - dac 9f+t - jms dsktrans; -4096; 4096; 9f+t; dskswap - isz dskswap - jmp dskswap i + cll; als 3 " get process disk address + dac 9f+t " save in t0 + jms dsktrans; -64; userdata; 9f+t; dskswap " read/write user area + lac 9f+t " get swap addr back + tad o20 " advance by 16?? + dac 9f+t " save + jms dsktrans; -4096; 4096; 9f+t; dskswap " read/write user memory + isz dskswap " skip bits + jmp dskswap i " return t = t+1 access: 0 lac i.flags - lmq - lac u.uid - spa - jmp access i - sad i.uid - lrs 2 - lacq - and mode " mode from system call - sza - jmp access i - jms error + lmq " save in MQ + lac u.uid " get user id + spa " negative? + jmp access i " yes: super user, return + sad i.uid " compare to file owner + lrs 2 " same: shift flags down two + lacq " get flags back + and mode " mode from system call + sza " access allowed? + jmp access i " yes: return + jms error " no: return error from system call fassign: 0 - -10 - dac 9f+t + -10 " loop count + dac 9f+t " in t0 1: - lac 9f+t - tad d10 - jms fget + lac 9f+t " get count + tad d10 " turn into fd + jms fget " fetch open file into "fnode" jms halt " will not happen - lac f.flags - sma - jmp 1f - isz 9f+t + lac f.flags " get fnode flags + sma " sign bit set (active)? + jmp 1f " no: free + isz 9f+t " increment loop count & loop until zero jmp 1b jmp fassign i 1: - lac mode - xor o400000 - dac f.flags - lac ii - dac f.i + lac mode " get mode from system call + xor o400000 " set sign bit + dac f.flags " save in fnode + lac ii " get i-number + dac f.i " save in fnode lac 9f+t - tad d10 - dac u.ac - dzm f.badd - jms fput - isz fassign + tad d10 " get fd + dac u.ac " return in user AC + dzm f.badd " clear file offset in fnode + jms fput " copy fnode back into u.ofiles + isz fassign " give skip return jmp fassign i t = t+1 + " load fnode (open file entry) from u.ofiles + " AC/ user fd + " jms fget + " bad fd + " return with fnode set fget: 0 - jms betwen; d0; d9 - jmp fget i - cll; mul; 3 + jms betwen; d0; d9 " fd 0..9? + jmp fget i " no, return + cll; mul; 3 " multiply by three lacq "** 01-s1.pdf page 29 - tad ofilesp - dac 9f+t - dac .+2 - jms copy; ..; fnode; 3 - isz fget + tad ofilesp " get pointer into u.ofiles + dac 9f+t " save in t0 + dac .+2 " save as copy source + jms copy; ..; fnode; 3 " copy to "fnode" + isz fget " give skip return jmp fget i + " copy fnode back to u.ofiles + " uses temp value set by "fget" + " (fget and fput calls must be paired) fput: 0 lac 9f+t dac .+3 diff --git a/src/sys/s6.s b/src/sys/s6.s index faaa13f..f1d07be 100644 --- a/src/sys/s6.s +++ b/src/sys/s6.s @@ -255,34 +255,37 @@ pget: 0 t = t+3 iwrite: 0 - dac 9f+t - lac iwrite + dac 9f+t " save arg in t0 + lac iwrite " load return address "** 01-s1.pdf page 38 - dac iread - lac cskp - dac iwrite + dac iread " save as iread return addr + lac cskp " load skip instruction + dac iwrite " save as iwrite instruction jmp 1f + " iread from file referenced by loaded inode + " AC/ file offset + " jms iread; addr; count iread: 0 - dac 9f+t - lac cnop - dac iwrite + dac 9f+t " save offset in t0 + lac cnop " get nop + dac iwrite " save as iwrite instruction 1: -1 - tad iread i - dac 10 + tad iread i " get word before return addr + dac 10 " store in index 10 & 11 dac 11 - isz iread - lac iread i - dac 9f+t+1 - isz iread + isz iread " increment return addr + lac iread i " load addr + dac 9f+t+1 " save in t1 + isz iread " increment return addr lac o70000 - xct iwrite - lac i.size + xct iwrite " skip if write + lac i.size " read: get file size cma - tad 9f+t + tad 9f+t " add offset cma jms betwen; d0; 9f+t+1 lac 9f+t+1 @@ -336,6 +339,11 @@ cskp: jmp 1b t = t+4 + " system call helper + " AC/ fd + " jms finac + " return with: fnode and inode loaded + " or makes error return to user finac: 0 lac u.ac jms fget @@ -347,6 +355,7 @@ finac: 0 jms iget jmp finac i + " update inode file size with value in AC dacisize: 0 dac i.size jms iput diff --git a/src/sys/s7.s b/src/sys/s7.s index 46e53ac..81034bb 100644 --- a/src/sys/s7.s +++ b/src/sys/s7.s @@ -1,8 +1,9 @@ "** 01-s1.pdf page 41 " s7 -pibreak: " priority interrupt processing "chain" - dac .ac "** CROSSED OUT.... +pibreak: " priority interrupt break processing "chain" + dac .ac " save interrupt AC + "** CROSSED OUT.... dpsf jmp 1f @@ -36,11 +37,13 @@ pibreak: " priority interrupt processing "chain" isz uquant " increment user quantum counter "** written: ttydelay -> ttyd1 "** written: ttyrestart -> ttyres1 + + " referenced in iread: cnop: nop -1 - dac 7 - clon + dac 7 " set location 7 to -1 + clon " enable clock interrupts, reset flag lac ttydelay spa isz ttydelay @@ -103,16 +106,16 @@ dsprestart: rlpd jmp piret -1: ksf - jmp 1f +1: ksf " (TTY) keyboard flag set? + jmp 1f " no lac ttydelay sma isz ttydelay - krb - dac char - sad o375 - jmp intrp1 + krb " read keyboard buffer + dac char " save in char + sad o375 " interrupt char ('}'?) + jmp intrp1 " yes lac d1 jms putchar dzm char @@ -172,14 +175,15 @@ ttyrestart: 0 dac sfiles+1 jmp ttyrestart i "** written arrow up 2 copies -1: sck "** BEGIN CROSSED OUT - jmp 1f + "** BEGIN CROSSED OUT +1: sck " Graphic-2 keyboard flag set? + jmp 1f " no. - cck - lck + cck " yes: clear flag + lck " read character dac char - sad o33 - jmp intrp2 + sad o33 " code 33 (ESCAPE?) + jmp intrp2 " yes: mark interrupt lac d3 jms putchar nop @@ -188,8 +192,8 @@ ttyrestart: 0 dac sfiles+2 jmp piret -1: rsf - jmp 1f +1: rsf " paper tape ready? + jmp 1f " no "** 01-s1.pdf page 44 @@ -235,12 +239,12 @@ ttyrestart: 0 dac sfiles+3 jmp piret -1: psf - jmp 1f +1: psf " paper tape ready? + jmp 1f " no - pcf + pcf " clear ptp flag lac d5 - jms getchar + jms getchar " get next char jmp .+3 psa jmp piret @@ -249,11 +253,12 @@ ttyrestart: 0 dac sfiles+4 jmp piret -1: spb "** BEGIN CROSSED OUT - jmp 1f + "** BEGIN CROSSED OUT +1: spb " graphic 2 push button flag set? + jmp 1f " no - cpb - lpb + cpb " clear push button flag + lpb " load push button value dac pbsflgs+1 "** 01-s1.pdf page 45 @@ -269,8 +274,8 @@ ttyrestart: 0 wbl jmp piret "** END CROSSED OUT -1: crsf - jmp 1f +1: crsf " card reader flag set? + jmp 1f " no crrb dac crchar @@ -278,14 +283,14 @@ ttyrestart: 0 dac crread jmp piret -1: crrb +1: crrb " read card reader buffer?? -piret: - lac 0 - ral - lac .ac - ion - jmp 0 i +piret: " return from priority interrupt + lac 0 " get LINK/PC + ral " restore LINK + lac .ac " restore AC + ion " reenable interrupts + jmp 0 i " return from interrupt wakeup: 0 dac 9f+t @@ -325,9 +330,9 @@ putcr: 0 cla jmp putcr i -intrp1: - lac d6 - dac .int1 +intrp1: " here with keyboard interrupt + lac d6 " get keyboard special device number + dac .int1 " save as interrupt source lac d1 jms getchar skp diff --git a/src/sys/s8.s b/src/sys/s8.s index 20d13f9..afce88c 100644 --- a/src/sys/s8.s +++ b/src/sys/s8.s @@ -8,14 +8,15 @@ dspbsz = 270 ndskbs = 4 " flags -.insys: 0 -.int1: 0 -.int2: 0 -.ac: 0 -.savblk: 0 -.dsptm: 0 -.dskb: 0 -.dske: 0 + " interupt flags +.insys: 0 " "in system" +.int1: 0 " inode for interrupt 1 +.int2: 0 " inode for interrupt 2 +.ac: 0 " saved AC from interrupt +.savblk: 0 " set by system call, cleared by disk i/o +.dsptm: 0 " display restart countdown (10 ticks) +.dskb: 0 " set on disk interrupt +.dske: 0 " status from disk interrupt " pointers tadu: tad ulist @@ -110,25 +111,25 @@ dspbuf: .=.+30 coldentry: dzm 0100 " not re-entrant - caf - ion - clon - law 3072 + caf " clear all flags + ion " enable interrupts + clon " clear clock flag + law 3072 " initialize display.... wcga jms dspinit law dspbuf jms movdsp - cla + cla " read system block from disk jms dskio; 06000 - jms copy; dskbuf; sysdata; ulist-sysdata - lac d3 + jms copy; dskbuf; sysdata; ulist-sysdata " copy to system data + lac d3 " look for "init" in default directory jms namei; initf jms halt "** 01-s1.pdf page 50 jms iget cla - jms iread; 4096; 4096 - jmp 4096 + jms iread; 4096; 4096 " read in "init" + jmp 4096 " start process 1 . = dspbuf+dspbsz+3 dskbuf = 07700 dskbs: .=.+65+65+65+65 @@ -144,9 +145,9 @@ name: .=.+4 lnkaddr: .=.+1 char: .=.+1 dskaddr: .=.+1 -uniqpid: 1 -lu: .=.+4 -sfiles: .=.+10 +uniqpid: 1 " pid generator +lu: .=.+4 " user (process) table entry copy +sfiles: .=.+10 " wait addresses for special files dpdata: dpstat: .=.+1 dpread: .=.+1 @@ -158,12 +159,24 @@ dspdata: crdata: crread: .=.+1 crchar: .=.+1 -sysdata: - s.nxfblk: .=.+1 - s.nfblks: .=.+1 " number of free blocks - s.fblks: .=.+10 " free block numbers - s.uniq: .=.+1 " next unique value - s.tim: .=.+2 " (up)time(?) in 60Hz ticks (low, high) +sysdata: " system data 64 words saved to disk + s.nxfblk: .=.+1 " pointer to next free block?? + s.nfblks: .=.+1 " number of free blocks (in fblks?) + s.fblks: .=.+10 " cached free block numbers + s.uniq: .=.+1 " next unique value + s.tim: .=.+2 " (up?)time in 60Hz ticks (low, high) + " process table + " first word + " bits 0:2 -- status + " 0: free slot + " 1: in/ready + " 2: in/notready + " 3: out/ready + " 4: out/notready?? + " bits 3:17 -- disk swap address/8 + " second word: process pid + " third word: used for smes/rmes + " fourth word: ?? ulist: 0131000;1;0;0 0031040;0;0;0 @@ -175,31 +188,34 @@ ulist: 0031340;0;0;0 0031400;0;0;0 0031440;0;0;0 -userdata: +userdata: " "ustruct" (swappable) u.ac: 0 " user AC u.mq: 0 " user MQ - u.rq: .=.+9 " user 010-017 + u.rq: .=.+9 " user 010-017, user PC u.uid: -1 " user id u.pid: 1 " process id u.cdir: 3 " connected directory (inode number?) - u.ulistp: ulist - u.swapret: 0 - u.base: 0 - u.count: 0 + u.ulistp: ulist " pointer to process table entry + u.swapret: 0 " kernel routine to resume at after swap in + u.base: 0 " start of user buffer + u.count: 0 " size of user buffer "** 01-s1.pdf page 51 - u.limit: 0 - u.ofiles: .=.+30 + u.limit: 0 " end of user buffer + u.ofiles: .=.+30 " open files (10 "fnode" entries) u.dspbuf: 0 u.intflg: 1 .=userdata+64 -ii: .=.+1 -inode: +ii: .=.+1 " number of i-node in inode: +inode: " disk inode in memory: i.flags: .=.+1 " inode flags " 400000 free?? (checked/toggled by icreat) " 200000 large file - " 000040 special? (checked by read/write) + " 000040 special device (indicated by inum)? " 000020 directory - " 000017 can be changed by chmod. + " 000010 owner read + " 000004 owner write + " 000002 world read + " 000001 world write i.dskps: .=.+7 " disk block pointers (indirect if "large file") i.uid: .=.+1 " owner i.nlks: .=.+1 " link count @@ -212,8 +228,11 @@ dnode: " directory entry: d.name: .=.+4 " name (space padded) d.uniq: .=.+1 " unique number from directory inode . = dnode+8 -fnode: - f.flags: .=.+1 - f.badd: .=.+1 - f.i: 0 - +fnode: " open file entry + f.flags: .=.+1 " see below + f.badd: .=.+1 " offset + f.i: 0 " file i-number +" f.flags: +" 400000 in use +" 000002 read +" 000001 write diff --git a/tools/a7out b/tools/a7out index 61f6e28..3ba7dff 100755 --- a/tools/a7out +++ b/tools/a7out @@ -485,15 +485,25 @@ sub eae { $PC++; return; } - if ( $maskedinstr == 0660700 ) { # alss: long left shift, signed - # We don't fill the lsb with LINK yet + if ( $maskedinstr == 0660700 ) { # alss: AC left shift, signed dprintf( "alss AC %06o step %d\n", $AC, $step ); $AC = ( $AC << $step ) & MAXINT; + $LINK = ( $AC << 1 ) & LINKMASK; $PC++; return; } - if ( $maskedinstr == 0640700 ) { # als: long left shift - dprintf( "alss AC %06o step %d\n", $AC, $step ); + if ( $maskedinstr == 0660600 ) { # llss: long left shift, signed + dprintf( "llss AC %06o step %d\n", $AC, $step ); + foreach my $i (1 .. $step) { + my $MQmsb= ($MQ & SIGN) ? 1 : 0; + $AC= (($AC << 1) | $MQmsb) & MAXINT; + $MQ= (($MQ << 1) | (($LINK) ? 1 : 0)) & MAXINT; + } + $PC++; + return; + } + if ( $maskedinstr == 0640700 ) { # als: AC left shift + dprintf( "als AC %06o step %d\n", $AC, $step ); $AC = ( $AC << $step ) & MAXINT; $PC++; return; @@ -551,7 +561,7 @@ sub cal { # 22 badcal # 23 capt # 24 rele - # 25 status + 25 => \&sys_status, # 26 badcal 27 => \&sys_smes, 28 => \&sys_rmes, @@ -653,12 +663,55 @@ sub sys_close { return; } +# Open something which could be a file or a directory +# Convert directories into files. Return the file handle. +sub opensomething { + my ($readorwrite, $filename )= @_; + my $tempfile= "/tmp/a7out.$$"; + my $FH; + + # If this is not a directory, simply open and return the FH + if (! -d $filename) { + open( $FH, $readorwrite, $filename ) || return(undef); + return($FH); + } + + # It's a directory. The on-disk format for this was: + # d.i: .=.+1 " inode number + # d.name: .=.+4 " name (space padded) + # d.uniq: .=.+1 " unique number from directory inode + # followed by two unused words + # The code creates a temporary file and fills in the i-node numbers + # and space padded filenames from the directory. The file is closed + # opened read-only and unlinked, and the open filehandle is returned. + opendir(my $dh, $filename) || return(undef); + open( $FH, ">", $tempfile) || return(undef); + dprintf("Converting directory $filename\n"); + + my @list= sort(readdir($dh)); + foreach my $name (@list) { + # Get the file's i-node number + my (undef,$inode)= stat($name); + + # ARGH! For now we are still read/writing ASCII files, so there's + # no way to represent a proper 18-bit value. For now I'll pad + # with spaces to create the record + printf( $FH " %-8s ", substr( $name, 0, 8 ) ); + } + closedir($dh); + close($FH); + open( $FH, "<", $tempfile) || return(undef); + unlink($tempfile); + return($FH); +} + # Common code for creat and open sub creatopen { my ($filename, $readorwrite)= @_; # Open the file - if ( open( my $FH, $readorwrite, $filename ) ) { + my $FH= opensomething($readorwrite, $filename ); + if ( $FH ) { # Find a place in the @FD array to store this filehandle. # 99 is arbitrary @@ -914,6 +967,61 @@ sub sys_time { return; } +# Status system call +sub sys_status { + # This seems to called as follows: + # law statbuf + # sys status; scrname; dd + # but I can't tell if PC+1 or PC+2 holds the filename pointer. + # For now, I'll use PC+1. $AC seems to hold the pointer to the statbuf + # which, as far as we can tell is: + # word 0: permission bits + # words 1-7: disk block pointers + # word 8: user-id + # word 9: number of links + # word 10: size in words + # word 11: uniq, I have no idea what this is. + # The permission bits are: + # 200000 large file, bigger than 4096 words + # 000020 directory + # 000010 owner read + # 000004 owner write + # 000002 user write + # 000001 user write + + # Get the start address of the string + # Convert this to a sensible ASCII filename + my $start = $Mem[ $PC + 1 ]; + my $filename = mem2arg($start); + dprintf( "status file %s statbuf %06o\n", $filename, $AC ); + + # Get the file's details + my (undef,undef,$mode,$nlink,$uid,undef,undef,$size)= stat($filename); + + # Set up the statbuf if we got a result + if ($nlink) { + $Mem[ $AC+8 ]= $uid & MAXINT; + $Mem[ $AC+9 ]= $nlink & MAXINT; + $Mem[ $AC+10 ]= $size & MAXINT; # Yes, I know, not words + + my $perms=0; + $perms= 01 if ($mode & 02); # World writable + $perms|= 02 if ($mode & 04); # World readable + $perms|= 04 if ($mode & 0200); # Owner writable + $perms|= 010 if ($mode & 0400); # Owner readable + $perms|= 020 if ( -d $filename); # Directory + $perms|= 0200000 if ($size > 4096); # Large file + $Mem[ $AC ] = $perms; + # Set AC to zero as we got something, else return -1 + $AC= 0; + } else { + $AC= MAXINT; + } + + $PC += 3; + return; +} + # Convert an 18-bit word into two ASCII characters and return them. # Don't return NUL characters sub word2ascii {