|
DataMuseum.dkPresents historical artifacts from the history of: Commodore CBM-900 |
This is an automatic "excavation" of a thematic subset of
See our Wiki for more about Commodore CBM-900 Excavated with: AutoArchaeologist - Free & Open Source Software. |
top - download
Length: 35991 (0x8c97) Types: TextFile Notes: UNIX file Names: »tmd.s«
└─⟦f27320a65⟧ Bits:30001972 Commodore 900 hard disk image with partial source code └─⟦2d53db1df⟧ UNIX Filesystem └─ ⟦this⟧ »sys/z8001/diag/tmd.s«
/ (lgl- / The information contained herein is a trade secret of Mark Williams / Company, and is confidential information. It is provided under a / license agreement, and may be copied or disclosed only under the / terms of that agreement. Any reproduction or disclosure of this / material without the express written authorization of Mark Williams / Company or persuant to the license agreement is unlawful. / / COHERENT Version 0.7.3 / Copyright (c) 1982, 1983, 1984. / An unpublished work by Mark Williams Company, Chicago. / All rights reserved. / -lgl) /* Configuration stuff that should go away eventually */ #define JR jp #define CLKENABLED 1 /* set to generate clock ticks */ #define LPENABLE 1 /* set to enable LP interrupts */ #define K1 1 /* 1K vs. 512 byte segment click size */ / Machine-dependent code in Coherent on the Commodore / M-series Z8001 processor running in segmented mode / (both user and system). UPASIZE = 2048 / Size of U-area in bytes NHUSEG = 48 / Number of hardware user segments EFAULT = 14 / Taken from <errno.h> SIGSYS = 7 / Taken from <msig.h> SIGBPT = 10 SIGSEG = 11 SIGEPA = 12 SIGPRV = 13 SIGNVI = 14 SIGNMI = 15 #if NLD .globl SS #else .globl IS, DS, SS #endif USTACK = 0x0000 #if !NLD USEG = 0x0101 IS = 0x3030 DS = 0x3131 #else USEG = 0x0303 #endif ES = 0x3D3D / Must be OS-1 OS = 0x3E3E / Overlay (driver,buffer,clist,inode) seg SS = 0x3F3F sp = rr14 / stack pointer spo = r15 / stack pointer offset / argument off stack pointer #define args(n) SS|2+2*[n](spo) #define arglb(n) SS|3+2*[n](spo) / Commodore-specific hardware: / Definitions for the system clock which uses CT3 of Z8036 #1, / and Centronics Line Printer which uses ports of 8036 #1 and #2. MMU = 0x00FC / Access MMU # 1 (▶1b◀T#2 is unused) CLATCH = 0x0201 / 4-bit configuration latch SSTEP = 0x01 / single step bit #define HZ 100 /* Don't use line clock */ CLKVAL0 = 20000 / 4MHz / HZ / 2 CLKVAL1 = 30000 / 6 MHz / HZ / 2 LPIRQ = 0x70 / LP interrupt vector # ZCIO2 = 0x80 / Z8036 #2 base port MICR = 0x01 / Master interrupt control port MCCR = 0x03 / Master configuration control port CTIV = 0x09 / Interrupt vector CT3CS = 0x19 / Command and status CT3CH = 0x35 / time constant high CT3CL = 0x37 / Time constant low CT3MS = 0x3D / CT3 Mode Select PBMS = 0x51 / Port B Mode specification reg. PBHS = 0x53 / Port B Handshake specification PBDPP = 0x55 / Port B data path polarity PBDD = 0x57 / Port B data direction PBSIOC = 0x59 / Port B special I/O control PBDATA = 0x1D / Port B data PBPPR = 0x5B / Port B pattern polarity reg. PBPTR = 0x5D / Port B pattern transition PBPMR = 0x5F / Port B pattern mask reg. PBCS = 0x13 / Port B command and status PBIV = 0x07 / Port B interrupt vector PCDPP = 0x0B / Port C data path polarity PCDD = 0x0D / Port C data direction PCSIOC = 0x0F / Port C special I/O control PCDATA = 0x1F / Port C data / Bits in the FCW FCW_SEG = 0x8000 / Segmented mode SGBIT = 15 / Bit # of segmented/non-segmented bit FCW_SYS = 0x4000 / System (vs. normal) mode SBIT = 14 / Bit # of system mode bit FCW_EPA = 0x2000 / Enable Extended processor FCW_VIE = 0x1000 / Enable vectored interrupts VIBIT = 12 FCW_NVIE = 0x0800 / Enable non-vectored interrupts NVIBIT = 11 TFCW = FCW_SEG+FCW_SYS / New FCW after traps / The Program Status Area. This file must be loaded / first and this data must be first in order to / ensure that it is on a mod-256 byte boundary. / The format of this table is suitable only for / Z8001 and Z8003 processors. The z8002 uses fewer / bytes because there are no segment numbers. / This table is in code because that is the way the / hardware fetches it. .shri .globl cmdblk_ cmdblk_: ldar rr2, start / Gets segment number properly jp (rr2) .blkb [3*512]+cmdblk_-. / cmdblk size is 3x512 byte blocks / first is for command blocks, / second is for ECC buffer, / third is an i/o buffer. psa: ldar rr2, start / Jump to system jp (rr2) .word 0 / round out to 4 words .long TFCW / Extended instruction trap .long tepa .long TFCW / Privileged instruction trap .long tprv .long TFCW / System call trap .long tsys .long TFCW / Segment trap .long tseg .long TFCW / Non-maskable interrupt .long tnmi .long TFCW / Non-vectored interrupt .long tnvi / Vectored interrupts .long TFCW / FCW for all vectored interrupts psavi: .long vint / Vector #0 (clock) .long vint / Vector #2 .long vint / Vector #4 .long vint / Vector #6 .long vint / Vector #8 .long vint / Vector #10 .long vint / Vector #12 .long vint / Vector #14 .long vint / Vector #16 .long vint / Vector #18 .long vint / Vector #20 .long vint / Vector #22 .long vint / Vector #24 .long vint / Vector #26 .long vint / Vector #28 .long vint / Vector #30 .long vint / Vector #32 .long vint / Vector #34 .long vint / Vector #36 .long vint / Vector #38 .long vint / Vector #40 .long vint / Vector #42 .long vint / Vector #44 .long vint / Vector #46 .long vint / Vector #48 .long vint / Vector #50 .long vint / Vector #52 .long vint / Vector #54 .long vint / Vector #56 .long vint / Vector #58 .long vint / Vector #60 .long vint / Vector #62 .long vint / Vector #64 .long vint / Vector #66 .long vint / Vector #68 .long vint / Vector #70 .long vint / Vector #72 .long vint / Vector #74 .long vint / Vector #76 .long vint / Vector #78 .long vint / Vector #80 .long vint / Vector #82 .long vint / Vector #84 .long vint / Vector #86 .long vint / Vector #88 .long vint / Vector #90 .long vint / Vector #92 .long vint / Vector #94 .long vint / Vector #96 .long vint / Vector #98 .long vint / Vector #100 .long vint / Vector #102 .long vint / Vector #104 .long vint / Vector #106 .long vint / Vector #108 .long vint / Vector #110 .long vint / Vector #112 .long vint / Vector #114 .long vint / Vector #116 .long vint / Vector #118 .long vint / Vector #120 .long vint / Vector #122 .long vint / Vector #124 .long vint / Vector #126 .long vint / Vector #128 .long vint / Vector #130 .long vint / Vector #132 .long vint / Vector #134 .long vint / Vector #136 .long vint / Vector #138 .long vint / Vector #140 .long vint / Vector #142 .long vint / Vector #144 .long vint / Vector #146 .long vint / Vector #148 .long vint / Vector #150 .long vint / Vector #152 .long vint / Vector #154 .long vint / Vector #156 .long vint / Vector #158 .long vint / Vector #160 .long vint / Vector #162 .long vint / Vector #164 .long vint / Vector #166 .long vint / Vector #168 .long vint / Vector #170 .long vint / Vector #172 .long vint / Vector #174 .long vint / Vector #176 .long vint / Vector #178 .long vint / Vector #180 .long vint / Vector #182 .long vint / Vector #184 .long vint / Vector #186 .long vint / Vector #188 .long vint / Vector #190 .long vint / Vector #192 .long vint / Vector #194 .long vint / Vector #196 .long vint / Vector #198 .long vint / Vector #200 .long vint / Vector #202 .long vint / Vector #204 .long vint / Vector #206 .long vint / Vector #208 .long vint / Vector #210 .long vint / Vector #212 .long vint / Vector #214 .long vint / Vector #216 .long vint / Vector #218 .long vint / Vector #220 .long vint / Vector #222 .long vint / Vector #224 .long vint / Vector #226 .long vint / Vector #228 .long vint / Vector #230 .long vint / Vector #232 .long vint / Vector #234 .long vint / Vector #236 .long vint / Vector #238 .long vint / Vector #240 .long vint / Vector #242 .long vint / Vector #244 .long vint / Vector #246 .long vint / Vector #248 .long vint / Vector #250 .long vint / Vector #252 .long vint / Vector #254 / Interrupt dispatch table for loadable drivers. / This table is set/cleared by setivec/clrivec. / Some go to C routines, others to `vret' routine. / The clock (timer) is handled specially. / There are only 128 vectored interrupts on the z8001 / versus 256 on the z8002. .shrd .globl vecs_, vmaps_ vecs_: .long vret_ / Where setivec puts its vectors .blkl 127 / Remaining vectors vmaps_: .blkw 128 / Maps for overlay segment .shri / Segmentation prototype for the system / Segment 30 code is code size of coherent, system only / Segment 31 data is full 64K, system only / Segments 32 and 33 are clist and buffer-mapped segments / Segments 3C, 3D, 3E are extra and overlay segments, full 64K, system only / Segment 3F stack is 2K, system only / Other segments are CPU inhibited. sattr: .byte 0x02, 0x02, 0x02, 0x02, 0x04, 0x04, 0x04, 0x04 .byte 0x04, 0x04, 0x04, 0x04, 0x02, 0x02, 0x02, 0x02 slen: .byte 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00 .byte 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x07 .even .globl u_ #if NLD u_ = 0x3F000000 / SS | 0 #else u_ = SS|0 #endif / The ROM has set the machine up and initialised most / CPU and hardware things. The processor is running / in 1 MMU mode (segmented) with code in segment 0x30 / and data bootstrapped into segment 0x31. / We start by setting up a stack pointer at the / end of the U-area. .shri .globl etext_, edata_, end_ .globl corebot_, romconf_ .globl main_, commodore_, stand_ start: ldl rr14, $u_+UPASIZE / Put stack at end of U-area sub r0, r0 / Set user stack (only for emulator) ldctl NSPOFF, r0 ldctl NSPSEG, r0 ldl rr2, $vecs_ / vector #0 ldl rr4, rr2 / rr2 is pointer to ... inc r5, $4 / next vector entry ld r0, $127*2 / Fill all 128 vectors with ... ldir (rr4), (rr2), r0 / copy of vector #0 ldar rr0, clk / Special clock routine goes ... ldl vecs_+0, rr0 / into vector slot #0 ldar rr0, psa / This is in code segment ldctl PSAPSEG, r0 / System data is actually code here ldctl PSAPOFF, r1 / Load of system segment attributes. ldar rr2, sattr / Pointer to 16 attributes ldl rr0, $0x00300010 / segment IS, 16 segments soutb MMU+0x0100, rl0 / ld r4, $MMU+0x0E00 / Write attributes sotirb (r4), (rr2), r1 / Write out seg. attributes / Load system segment lengths. ldar rr2, slen / Pointer to 15 lengths ldl rr0, $0x0031000F / segment IS+1, 15 segments soutb MMU+0x0100, rl0 / Set segment number ld r4, $MMU+0x0D00 / Set limit field sotirb (r4), (rr2), r1 / Write out seg. limits call commodore_ / Do machine-specific initialisation / Set up U-area mapping at corebot ld r0, corebot_ / mapping base add r0, r0 / <<1 to hardware base #if K1 add r0, r0 / <<2 to hardware base #endif ld r1, $SS soutb MMU+0x0100, rh1 / Set segment number to U-area soutb MMU+0x0800, rh0 / Write base field high soutb MMU+0x0800, rl0 / Write field low call lpclk / Initialise clock and LP / Call main with interrupts on / When main returns, go into / user process at location 0. ei VI call main_ / Interrupts enabled di VI inc depth_ / Depth is one for user mode ld r0, $0xFFF0 / save top 16 bytes of user stack ldctl NSPOFF, r0 ld r0, $USTACK ldctl NSPSEG, r0 ldps 0f / Enter user mode for init process 0: .word 0, FCW_VIE+FCW_SEG / Non-segmented, user mode, int. on .word USEG, 0 / PC at 0 in User code segment / Never goes past here / Turn on clock. It is specially set to vector #0. / The clock is in Z8036 chip #1 which also has the / keyboard scanning port on it. / Initialise ports on 8036 #1 and #2 for centronics line printer. lpclk: #if CLKENABLED ld r0, $0x01 / Reset outb MICR, rl0 / Z8036 #1 outb ZCIO2+MICR, rl0 / Z8036 #2 outb MICR, rh0 / Turn off reset outb ZCIO2+MICR, rh0 / outb PBSIOC, rh0 / Init centronics data lines outb PBDPP, rh0 outb PBDD, rh0 outb PBDATA, rh0 outb PCDPP, rh0 / set port C non-inverting ld r0, $0x07 / pc0, pc1, & pc2 are input outb PCDD, rl0 / pc3 is output mode outb PCSIOC, rh0 / port C normal mode operation ld r0, $0x70 / mask to disable writing.... outb PCDATA, rl0 / on pc0, pc1, & pc2 #if HZ!=100 /* Line frequency */ ld r0, $0xa0 / Continous cycle, external (line) freq ld r1, $1 / 1 count for 60/50 HZ #else ld r0, $0x80 / Continuous single cycle ld r1, $CLKVAL0 / freq. at 1/2 PCLK rate 4 MHz test romconf_+14 / Clock type ? [romconf.rom_ctype] JR eq, 0f / branch if 4 Mhz ld r1, $CLKVAL1 / freq. at 1/2 PCLK rate 6 Mhz 0: #endif outb CT3MS, rl0 / Set counting and cycling options sub r2, r2 outb CTIV, rl2 / Vector #0 for clock outb CT3CH, rh1 / Set counter frequency outb CT3CL, rl1 / and low half ld r2, $0x20 / Clear IP and IUS outb CT3CS, rl2 ld r2, $0xC6 / Set IE, Trigger Counter, gate outb CT3CS, rl2 ldb rl0, $0x80 / Set master ... outb MICR, rl0 / interrupt enable ldb rl0, $0x90 / Enable counter timer #3 ... outb MCCR, rl0 / and port c ldb rl0, $0x06 / start it up... outb CT3CS, rl0 / trigger counter / Port 2 initialisation ld r0, $0x02 ld r1, $0x04 outb ZCIO2+PBMS, rl1 / OR pattern recognition mode outb ZCIO2+PBHS, rh0 outb ZCIO2+PBDPP, rl0 / Ack line should be inverted outb ZCIO2+PBDD, rl0 / Ack line is for input outb ZCIO2+PBSIOC, rl0 / Normal I/O outb ZCIO2+PBPPR, rl0 / Interrupt on ACK 1 outb ZCIO2+PBPTR, rh0 outb ZCIO2+PBPMR, rl0 ldb rl1, $LPIRQ / Vector # outb ZCIO2+PBIV, rl1 / Set into chip ldb rl1, $0x20 outb ZCIO2+PBCS, rl1 / Clear IP & IUS ldb rl1, $0xC0 outb ZCIO2+PBCS, rl1 / Enable port B interrupts outb ZCIO2+PCDPP, rh0 / Strobe should not be inverted outb ZCIO2+PCDD, rh0 / Strobe is output outb ZCIO2+PCSIOC, rh0 / Normal I/O ld r1, $0x08 outb ZCIO2+PCDATA, rl1 / Strobe starts out as 1 outb ZCIO2+CT3MS, rh0 / No counter/timer ldb rl1, $0x80 #if LPENABLE outb ZCIO2+MICR, rl1 / Master interrupt enable #endif ldb rl1, $0x90 outb ZCIO2+MCCR, rl1 / Enable ports B and C #endif ret / Trap and interrupt linkage / All routines end up building approximately the / same type of linkage information on the stack / so that higher level parts of the system do / most of the sophisticated work. The stack looks / like this when C `trap' has been called: / / |----------| / |pc offset | / |----------| / |pc segment| / |----------| / | fcw | / |----------| / | id | pushed by hardware / |----------| / |trap type | real for traps--junk for interrupts / |----------| / | r15 | / |----------| / ... / |----------| / | r0 | / |----------| / | NSP OFF | / |----------| / | NSP SEG | / |----------| / | OS Map | / |----------| / | ES Map | / |----------| / tepa: push (sp), $SIGEPA / Extended processor trap JR 0f tprv: push (sp), $SIGPRV / Privileged instruction JR 0f tnmi: push (sp), $SIGNMI / Non-maskable interrupt JR 0f / Non-vectored interrupt is for single-stepping in / ptrace system call. Fiddle with the saved FCW (turn / off NVIE, enable VI) tnvi: set SS|2(spo), $VIBIT / Enable vectored interrupt and ... res SS|2(spo), $NVIBIT / disallow more steps by default push (sp), r0 / Save a register sub r0, r0 / Reset single stepper bit outb CLATCH, rl0 pop r0, (sp) / Restore r0 push (sp), $SIGBPT / Generate a breakpoint JR 0f / Special case for system mode segmentation violations / If so, and the PC points to certain special user-system / block move routines, then arrange so that the faulting / routine cleanly return an error. tseg: ex r0, (sp) / Save r0 sinb rl0, MMU+0x0200 / Read violation type register ex r0, (sp) / into identifier and put r0 back soutb MMU+0x1100, rl0 / Reset violation bit SS|2(spo), $SBIT / System mode? JR eq, 1f / User mode, not checked here cp SS|6(spo), $pgetw_ / In special user-system JR ult, 1f / character-at-a-time routines? cp SS|6(spo), $kkcopy_ / JR uge, 2f / No -- check for block moves ex r0, SS|2(spo) / ldctl FCW, r0 / Restore FCW ex r0, SS|2(spo) / inc spo, $8 / Clear trap from stack ldb u_, $EFAULT / Set u.u_error code ld r1, $-1 / return(-1) ret / Return to caller 2: cp SS|6(spo), $kkfix / in block move routine? JR uge, 1f / no ex r0, SS|2(spo) / ldctl FCW, r0 / Re-load FCW ex r0, SS|2(spo) / inc spo, $8 / clear away trap dec r5 / Compensate for last faulting ld byte ldb u_, $EFAULT / Set u.u_error jp kkfix / Let it clean up 1: push (sp), $SIGSEG / Segmentation violation JR 0f / Common processing for all traps that haven't been / filtered out. Build a special calling sequence for / the C routine `trap'. This routine also handles `ddt'. tsys: push (sp), $SIGSYS / System call 0: dec depth_ / Down count soft interrupt level sub spo, $32 / Save all registers. ldm (sp), r0, $16 ldctl r0, NSPSEG / Save normal mode sp segment ldctl r1, NSPOFF / Save part of normal mode sp pushl (sp), rr0 #if DDT ldl ddtregp_, sp / Allows ddt to find registers #endif ld r0, $ES / Set ES segment soutb MMU+0x0100, rh0 / ES + OS maps sinb rh3, MMU+0x0C00 / ES high sinb rl3, MMU+0x0C00 / ES low sinb rh2, MMU+0x0C00 / OS high sinb rl2, MMU+0x0C00 / OS low pushl (sp), rr2 / push ES+OS ei VI call trap_ / At last get to C JR 0f / Dismiss trap / All vectored interrupts from devices come here. / The id comes from the vector # and is an even / integer in the range 0-254. A fake trap type / is used to allow interrupts and traps to / return through the same code. vint: sub spo, $32+2 / Save registers and fake type ldm (sp), r0, $14 / Save them (but not system SP) ldctl r0, NSPSEG / Save normal sp seg ldctl r1, NSPOFF / Save offset of normal sp pushl (sp), rr0 ld r0, $ES / Save OS and ES soutb MMU+0x0100, rh0 / segment value sinb rh3, MMU+0x0C00 / ES high sinb rl3, MMU+0x0C00 / ES low sinb rh2, MMU+0x0800 / OS high (no incr) sinb rl2, MMU+0x0800 / OS low (no incr) pushl (sp), rr2 / push OS+ES dec depth_ / Down count int. level ld r2, SS|42(spo) / Identifier of vector (0-255) and r2, $0xFE / Make even, low byte push (sp), r2 / Push id for interrupt routine ld r0, vmaps_(r2) / Vector mappings for OS soutb MMU+0x0800, rh0 / high base soutb MMU+0x0800, rl0 / Low base ei VI / Interrupts on after depth fixed add r2, r2 / <<1 to make room for 4-byte table ldl rr2, vecs_(r2) / call (rr2) / Call interrupt routine inc spo, $2 / Throw away junk / Dismiss sequence. / This is common sequence for interrupts and traps. / Attempt to reschedule. If returning to normal mode, / run low priority part of clock (stand). / Restore context and return. 0: test depth_ / Depth==0 means normal mode JR ne, 1f / Don't call stand if system mode ldl rr0, cprocp_ / SELF==NULL ignored cpl rr0, iprocp_ / Idle process interrupted? JR eq, 0f bit SS|44(spo), $SBIT / Retuurning to kernel process? JR ne, 1f / Then don't call stand 0: call stand_ / Low-priority timers, etc. 1: di VI / Keep out vectored interrupts inc depth_ / interrupts off from this to iret popl rr0, (sp) / rr0 = OS+ES ld r2, $ES / Restore OS + ES ... soutb MMU+0x0100, rh2 / segment maps soutb MMU+0x0C00, rh1 / ES high soutb MMU+0x0C00, rl1 / ES low soutb MMU+0x0C00, rh0 / OS high soutb MMU+0x0C00, rl0 / OS low popl rr2, (sp) / rr2 = NSP ldctl NSPSEG, r2 / Segment of NSP ldctl NSPOFF, r3 ldm r0, (sp), $14 / Re-load R0-R13 add spo, $32+2 / Pop saved regs + trap type / When NVIE bit in FCW is on, this indicates / a ptrace single step request has just been issued. / Turn off the normal interrupts and arm the special / delay circuit in the System Control latch. This / will then go to `tnvi' after 1 user instruction has / been issued which will turn vectored interrupts on again. bit SS|2(spo), $NVIBIT / Is it ptrace? [BUG] JR eq, 0f / no res SS|2(spo), $VIBIT / Turn off vectored interrupts ld r0, $SSTEP / Set System Control Latch to ... outb CLATCH, rl0 / Single stepper (just before iret) 0: iret / At last / Special routine that interfaces to C clock routine. / diddles the clock hardware to dismiss the interrupt / and pushes user/system mode and user PC. / The PC and FCW are those pushed by the hardware and / found again by us by magic groping around! / / Also empty interrupt dispatch routine .globl vret_, clock_ clk: push (sp), $0 / Assume system mode bit SS|52(spo), $SBIT / Correct ? JR ne, 0f / Yes inc (sp) / Make normal (user) mode 0: pushl (sp), SS|54(spo) / User PC offset for profiler call clock_ / Call portable code inc spo, $6 / Clean up ld r0, $0x24 / Clear IP and IUS to ... outb CT3CS, rl0 / dismiss the interrupt #if 0 vret_: #endif ret / halt() / Called from panic and anywhere else / as an entry-point to `ddt'. Fakes up / a trap sequence on the stack. .globl halt_ halt_: #if DDT push (sp), r0 ld r0, $UPASIZE-46 / Pushed stuff just below NSP ld ddtregp_+2, r0 pop r0, (sp) JR ddt_ #else di VI JR . #endif / Routines to do the context switch. The / switch takes two forms: / 1) an environment (`MENV') / 2) an context (`MCON') which requires reloading / of the U-area base as well as the rest of the / environment. / Except for a little bit of futsing around, these / two are really quite the same thing. / The switch assumes that C will save the other / registers in the course of things. / consave(ptr) / MCON *ptr; / / conrest(u, o) / saddr_t u; / unsigned o; / / envsave(ptr) / MENV *ptr; / / envrest(ptr) / MENV *ptr; .globl consave_, conrest_ .globl envsave_, envrest_ envsave_: consave_: ldl rr2, args(1) / pointer ldm (rr2), r6, $10 / save r6-r15 add r3, $32 / end of structure ld r1, $ES / Segment for ES + OS ldctl r4, FCW / Save FCW before disable di VI soutb MMU+0x0100, rh1 / set segment sinb rh0, MMU+0x0C00 / ES high sinb rl0, MMU+0x0C00 / ES low sinb rh1, MMU+0x0C00 / OS high sinb rl1, MMU+0x0C00 / OS low ldctl FCW, r4 / Restore state pushl (rr2), rr0 / Save it away ld r5, depth_ / Save interrupt depth pushl (rr2), rr4 / Save FCW & depth pushl (rr2), (sp) / Save PC segment + offset sub r1, r1 / return 0 here ret envrest_: ldl rr2, args(1) / pointer JR 0f conrest_: dec depth_ / Fake depth/no return to user ldl rr0, args(1) / r0=user area, r1=offset lda rr2, u_(r1) / Address of context in U-area di VI soutb MMU+0x0100, rh2 / Set map for SS segment add r0, r0 / saddr_t <<1 to hardware #if K1 add r0, r0 / saddr_t <<2 to hardware #endif soutb MMU+0x0800, rh0 / Load base address .. soutb MMU+0x0800, rl0 / of SS segment 0: ldm r6, (rr2), $10 / Re-load r6-r15 add r3, $20 / after saved registers popl (sp), (rr2) / Save PC segment & offset popl rr4, (rr2) / Restore FCW and depth. ld depth_, r5 / load depth, save FCW for later ld r1, $ES / Map segment di VI / MMU load indivisible soutb MMU+0x0100, rh1 / Segment # popl rr0, (rr2) / GET ES & OS maps soutb MMU+0x0C00, rh0 / ES high soutb MMU+0x0C00, rl0 / ES low soutb MMU+0x0C00, rh1 / OS high soutb MMU+0x0C00, rl1 / OS low ldctl FCW, r4 / Re-load FCW from long ago ld r1, $1 / return 1 ret / spl(fcw) / Stuff new `fcw' into the FCW and return the / old one. This is used to implement splo(), and / spl(f) .globl spl_, sphi_ spl_: ldctl r1, FCW / Get old FCW ld r0, args(1) / Reload new FCW ldctl FCW, r0 ret sphi_: ldctl r1, FCW / Old FCW di VI ret / I/O Instructions / This is very messy on this machine because / there are so many instructions, most of which / might be used once or twice. What we do is / to provide in/out for byte/word for normal i/o / space. Special I/O is done only through assembler / routines for accessing the MMU. / Usage: / in(port) inb (port) / out(port, valueue) outb(port, value) / / Block moves in/out for words/bytes. / inwcopy(port, paddr, nb) inbcopy(port, paddr, nb) / unsigned port, nb; unsigned port, nb; / paddr_t paddr; paddr_t paddr; / / outwcopy(port, paddr, nb) outbcopy(port, paddr, nb) / unsigned port, nb; unsigned port, nb; / paddr_t paddr; paddr_t paddr; / / outwcopy and outbcopy should have underbars after their name. .globl in_, inb_ .globl out_, outb_ .globl inwcopy_, inbcopy_ .globl outwcopy_, outbcopy_ in_: ld r1, args(1) in r1, (r1) ret inb_: ld r1, args(1) inb rl1, (r1) subb rh1, rh1 ret out_: ld r1, args(1) / port ld r0, args(2) / value out (r1), r0 ret outb_: ld r1, args(1) / port ld r0, args(2) / value outb (r1), rl0 ret inwcopy_: ld r1, args(1) / port address ldl rr2, args(2) / destination address ld r0, args(4) / number of bytes srl r0, $1 / convert to words inir (rr2), (r1), r0 ret inbcopy_: ld r1, args(1) / port address ldl rr2, args(2) / destination address ld r0, args(4) / number of bytes inirb (rr2), (r1), r0 ret outwcopy_: ld r1, args(1) / port address ldl rr2, args(2) / source address ld r0, args(4) / number of bytes srl r0, $1 / convert to words otir (r1), (rr2), r0 ret outbcopy_: ld r1, args(1) / port address ldl rr2, args(2) / source address ld r0, args(4) / number of bytes otirb (r1), (rr2), r0 ret / Cross-space move routines, character, block, / word, and pointer. There is a subterfuge to / make segmented and unsegmented things look / similar. Also the order of these routines is / important. From pgetw to kkcopy is checked / by the segmentation violation handler to / return faults on stuff copied to/from another / space. From `kkcopy_' to `ppfix' is also handled / specially. All other system mode segmentation / violations panic the system. / Get/put words/pointers/bytes into a physical space. / Macros in header files make this either user space / or auxiliary segment. / / pgetw(p) pgetp(p) pgetb(p) / char *p; char *p; char *p; / / pputw(p, i) pputp(p, ptr) pputb(p, c) / char *p; char *p; char *p; / int i; char *ptr; char c; / .globl pgetw_, pgetp_, pgetb_ .globl pputw_, pputp_, pputb_ pgetw_: ldl rr2, args(1) / address ld r1, (rr2) / fetch value ret pgetb_: ldl rr2, args(1) / address sub r1, r1 ldb rl1, (rr2) ret pgetp_: ldl rr2, args(1) / address ldl rr0, (rr2) / fetch pointer ret pputw_: ldl rr2, args(1) / address ld r1, args(3) / value ld (rr2), r1 / store away ret pputb_: ldl rr2, args(1) / address ld r1, args(3) / value ldb (rr2), rl1 / store away ret pputp_: ldl rr2, args(1) / address ldl rr0, args(3) / pointer value ldl (rr2), rr0 / store away ret / Kernel (system) address space copy and clear routines / NOTE: for speed, ukcopy has a special entry point here. / / kclear(bp, n) kkcopy(f, t, n) / char *bp; char *f, *t; / unsigned n; unsigned n; .globl kclear_, kkcopy_, ukcopy_ ukcopy_: ldl rr2, args(1) / from pointer and r2, $~0xE0FF / ufix it JR 0f kkcopy_: ldl rr2, args(1) / from pointer 0: ldl rr4, args(3) / to pointer ld r0, args(5) / count test r0 JR eq, kkfix / No words to copy #if 1 ld r1, r0 / Test for even counters ... or r1, r3 / and addresses or r1, r5 / rr r1 / Set carry if anything odd JR c, 1f / Branch if slow (odd) mode srl r0 / Divide by 2 ldir (rr4), (rr2), r0 / Do word copy JR kkfix 1: #endif ldirb (rr4), (rr2), r0 / Do word copy kkfix: ld r1, r5 / current destination offset sub r1, args(4) / beginning destination pointer ret kclear_: ldl rr2, args(1) / Kernel segmented pointer ld r0, args(3) / number of bytes srl r0 / to words JR nc, 1f / Branch if even clrb (rr2) / Clear first (odd) byte 1: dec r0 / Funny way copy occurs jr mi, 1f / None to copy clr (rr2) JR eq, 1f / Done if count <4 lda rr4, rr2(2) ldir (rr4), (rr2), r0 / clear memory 1: ret / Fixup a user address to prevent system accesses. / Map a paddr_t in segment `sn' and return a char * mapped in. / pfix is for setting up permanent segments that won't move much because / most segments are not context switched. / char * char * / pfix(sn, p)) ufix(p) / paddr_t p; char *p; / NOTE: ukcopy has its own version of ufix for speed. .globl pfix_, ufix_ pfix_: ldctl r4, FCW ldb rh0, arglb(1) / segment # to map ldl rr2, args(2) / paddr_t ld r1, r3 / offset #if K1 and r1, $0x3FF / within one saddr_t unit srll rr2, $10 / to saddr_t add r3, r3 / <<2 to hardware #else and r1, $0x1FF / within one saddr_t unit srll rr2, $9 / to saddr_t #endif add r3, r3 / ... di VI / Interrupts disabled for fiddling soutb MMU+0x0100, rh0 / Segment # soutb MMU+0x0800, rh3 / base high soutb MMU+0x0800, rl3 / base low ldctl FCW, r4 / Restore interrupts ret ufix_: ldl rr0, args(1) / ptr and r0, $~0xE0FF / Turn off bottom, non-user seg bits ret / turn a paddr_t (physical offset in bytes) into a vaddr_t (virtual addr) / and vice versa. / paddr_t vaddr_t / vtop(v) ptov(p) / vaddr_t v; paddr_t p; .globl vtop_, ptov_ vtop_: ldl rr0, args(1) ldb rl0, rh0 and r0, $0x007F ret ptov_: ldl rr0, args(1) ldb rh0, rl0 and r0, $0x7F00 ret / / Segment copy and clear routines. / A `saddr_t' is is units of 512 or 1024 bytes / but the hardware actually uses units of / 256 bytes, so it must be shifted left. / Clear `n' saddr_t units of memory / / sclear(a, n) / saddr_t a; / unsigned n; .globl sclear_ sclear_: call omapget_ / Get old overlay map ld r0, args(1) / Software clicks add r0, r0 / Hardware clicks #if K1 add r0, r0 / <<2 for 1K segment click #endif pushl (sp), rr0 / Save Hardware click and old map ld r2, $OS / Segment of rr2 pointer ld r4, r2 / segment of rr4 pointer 0: dec args(4) / Really arg 2 - click counter JR mi, 1f call omapset_ / Uses map value (sp), destroys rr0 sub r3, r3 / Zero offset of rr2 ld r5, $2 / Load 1 word offset from rr2 #if K1 ld r0, $512-1 / 512 words == 1024 bytes #else ld r0, $256-1 / 256 words == 512 bytes #endif clr (rr2) / clear first word ldir (rr4), (rr2), r0 / clear subsequent words #if K1 inc (sp), $4 / next click #else inc (sp), $2 / next click #endif JR 0b / loop until done 1: inc spo, $2 / Pop saddr from stack. call omapset_ / restore Overlay map inc spo, $2 ret / Copy segments of memory from one place to another. / / slrcopy(f, t, n) / saddr_t f, t; / unsigned n; .globl slrcopy_ slrcopy_: ldl rr0, args(1) / r0 = `f', r1 = `t' addl rr0, rr0 / adjust to hardware clicks. #if K1 addl rr0, rr0 / <<2 for 1K saddr_t #endif ld r2, args(3) / `n' test r2 / if zero, done JR eq, 9f / ldctl r3, FCW / Save interrupt state dec spo, $10 / Reserve space to ... ldm (sp), r6, $5 / Save some registers ld r4, $ES / Extra segment to MMU and address ld r8, $OS / OS segment # for address di VI / disable to fiddle soutb MMU+0x0100, rh4 / Save ES + OS sinb rh6, MMU+0x0C00 / ES high sinb rl6, MMU+0x0C00 / ES low sinb rh7, MMU+0x0C00 / OS high sinb rl7, MMU+0x0C00 / OS low ldctl FCW, r3 / Restore interrupts 0: di VI soutb MMU+0x0100, rh4 / Set ES + OS to do copy soutb MMU+0x0C00, rh0 / ES high soutb MMU+0x0C00, rl0 / ES low soutb MMU+0x0C00, rh1 / OS high soutb MMU+0x0C00, rl1 / OS low ldctl FCW, r3 / Restore interrupts #if K1 ld r10, $512 / word count of 1 click #else ld r10, $256 / word count of 1 click #endif sub r5, r5 / Virtual offset 0 in rr4 sub r9, r9 / Same for rr8 ldir (rr8), (rr4), r10 #if K1 inc r0, $4 / Next click inc r1, $4 / #else inc r0, $2 / Next click inc r1, $2 / #endif djnz r2, 0b / Cycle around di VI / Disable soutb MMU+0x0100, rh4 / Restore ES + OS soutb MMU+0x0C00, rh6 soutb MMU+0x0C00, rl6 soutb MMU+0x0C00, rh7 soutb MMU+0x0C00, rl7 ldctl FCW, r3 ldm r6, (sp), $5 / Restore registers inc spo, $10 9: ret / Extra map and overlay map manipulation routines. / omapset -- set the overlay segment map to the specified hardware map value. / omapget -- get the overlay segment value value / emapset -- same for the extra map / emapget -- get extra map / [NB: these routines only change r0 and r1!] / cmap_t / omapget() omapset(m) / cmap_t m; .globl omapget_, omapset_, emapget_, emapset_ omapset_: ld r1, $OS / segment number ... ldctl r0, FCW / Save interrupt state di VI soutb MMU+0x0100, rh1 / into MMU ld r1, args(1) / map value ... soutb MMU+0x0800, rh1 / high half of base soutb MMU+0x0800, rl1 / low half of base ldctl FCW, r0 / restore state ret emapset_: ld r1, $ES / segment number ... ldctl r0, FCW / Save interrupt state di VI soutb MMU+0x0100, rh1 / into MMU ld r1, args(1) / map value ... soutb MMU+0x0800, rh1 / high half of base soutb MMU+0x0800, rl1 / low half of base ldctl FCW, r0 / restore state ret omapget_: ld r1, $OS / segment number ... ldctl r0, FCW / Save interrupt state di VI soutb MMU+0x0100, rh1 / into MMU sinb rh1, MMU+0x0800 / high half of base sinb rl1, MMU+0x0800 / low half ldctl FCW, r0 / restore state ret emapget_: ld r1, $ES / segment number ... ldctl r0, FCW / Save interrupt state di VI soutb MMU+0x0100, rh1 / into MMU sinb rh1, MMU+0x0800 / high half of base sinb rl1, MMU+0x0800 / low half ldctl FCW, r0 / restore state ret / Load `nseg' segments of the MMU, starting at User segment base (segment 0) / Clear the rest of `NHUSEG' segments from access. / / loadmmu(nseg, p) / unsigned segno, nseg; / char p[4*32]; .globl loadmmu_ loadmmu_: ld r0, args(1) / r0 = number of segments ld r5, $NHUSEG / Total segments sub r5, r0 / Number to clear at end add r0, r0 / add r0, r0 / * sizeof each descriptor ldl rr2, args(2) / Pointer to segment descriptors. sub r1, r1 / Segment # 0 ldctl r4, FCW / Save interrupt state di VI soutb MMU+0x0100, rh1 / Start segment # soutb MMU+0x2000, rl1 / Clear descriptor counter (in case) ld r1, $MMU+0x0F00 / command write descriptors and sotirb (r1), (rr2), r0 / increment test r5 JR eq, 1f / branch if none to clear ldar rr2, cseg / Clear segments ld r1, $MMU+0x0E00 / Write attribute and increment SAR sotirb (r1), (rr2), r5 / 1: ldctl FCW, r4 / Restore interrrupts ret / Clear segments descriptors with CPU inhibit cseg: .byte 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04 .byte 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04 .byte 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04 .byte 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04 .byte 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04 .byte 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04 / Get the values in the MMU. / / getmmu(segno, nseg, p) / unsigned segno, nseg; / char *p; .globl getmmu_ getmmu_: ldl rr0, args(1) / r0=segno, r1=nseg ldl rr2, args(3) / pointer to returned data add r1, r1 / r1<<2 for ... add r1, r1 / sizeof table soutb MMU+0x0100, rl0 / Set segment # in MMU soutb MMU+0x2000, rh0 / Clear descriptor counter ld r4, $MMU+0x0F00 / command to read descriptors... sinirb (rr2), (r4), r1 / increment ret / / Miscellaneous routines used by Coherent / / Return the high order half of a*b. Used / in scaling the profile data. / / pscale(a, b) / unsigned a, b; .globl pscale_ pscale_: /[FIX] ld r1, args(1) / a mult rr0, args(2) / a*b ld r1, r0 / return high half ret / Wait for next interrupt. / This is where Coherent lives most / of the time. .globl _idle_ _idle_: ei VI halt ret / System data area .shrd / Initial code that gets /etc/init / running in user mode. This is / in data space because it gets / copied into the user space later. .globl icodep_, icodes_ icode: subl rr0, rr0 / NULL pointer pushl (sp), rr0 ld r2, $USEG / Segment # ld r3, $argv-icode pushl (sp), rr2 ld r3, $file-icode pushl (sp), rr2 pushl (sp), rr0 / PC of routine sys 11 / exec .word 0xE8FF / jr . [BN!!] argv: .word USEG, file-icode .long 0 file: .ascii "/etc/init" .byte 0 .even icend: icodep_: .long icode icodes_: .word icend-icode .globl ddtregp_, depth_ ddtregp_: .blkl 1 / SP for finding registers depth_: .word 0 / Stack depth (whether user or sys)