792 lines
25 KiB
C
792 lines
25 KiB
C
/* pdp10_pag.c: PDP-10 paging subsystem simulator
|
||
|
||
Copyright (c) 1993-2001, Robert M Supnik
|
||
|
||
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
|
||
ROBERT M SUPNIK 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.
|
||
|
||
Except as contained in this notice, the name of Robert M Supnik shall not
|
||
be used in advertising or otherwise to promote the sale, use or other dealings
|
||
in this Software without prior written authorization from Robert M Supnik.
|
||
|
||
pag KS10 pager
|
||
|
||
03-May-01 RMS Fixed bug in indirect page table pointer processing
|
||
29-Apr-01 RMS Added CLRCSH for ITS, fixed LPMR
|
||
|
||
The pager consists of a standard hardware part (the translation
|
||
tables) and an operating-system specific page table fill routine.
|
||
|
||
There are two translation tables, one for executive mode and one
|
||
for user mode. Each table consists of 512 page table entries,
|
||
one for each page in the 18b virtual address space. Each pte
|
||
contains (in the hardware) a valid bit, a writeable bit, an
|
||
address space bit (executive or user), and a cacheable bit, plus
|
||
the physical page number corresponding to the virtual page. In
|
||
the simulator, the pte is expanded for rapid processing of normal
|
||
reads and writes. An expanded pte contains a valid bit, a writeable
|
||
bit, and the physical page number shifted left by the page size.
|
||
|
||
Expanded pte meaning
|
||
0 invalid
|
||
>0 read only
|
||
<0 read write
|
||
|
||
There is a third, physical table, which is used in place of the
|
||
executive and user tables if paging is off. Its entries are always
|
||
valid and always writeable.
|
||
|
||
To translate a virtual to physical address, the simulator uses
|
||
the virtual page number to index into the appropriate page table.
|
||
If the page table entry (pte) is not valid, the page fill routine
|
||
is called to see if the entry is merely not filled or is truly
|
||
inaccessible. If the pte is valid but not writeable, and the
|
||
reference is a write reference, the page fill routine is also
|
||
called to see if the reference can be resolved.
|
||
|
||
The page fill routine is operating system dependent. Three styles
|
||
of paging are supported:
|
||
|
||
TOPS10 known in the KS10 microcode as KI10 paging,
|
||
used by earlier versions of TOPS10
|
||
TOPS20 known in the KS10 microcode as KL10 paging,
|
||
used by later versions of TOPS10, and TOPS20
|
||
ITS used only by ITS
|
||
|
||
TOPS10 vs TOPS20 is selected by a bit in the EBR; ITS paging is
|
||
"hardwired" (it required different microcode).
|
||
*/
|
||
|
||
#include "pdp10_defs.h"
|
||
#include <setjmp.h>
|
||
|
||
/* Page table (contains expanded pte's) */
|
||
|
||
#define PTBL_ASIZE PAG_N_VPN
|
||
#define PTBL_MEMSIZE (1 << PTBL_ASIZE) /* page table size */
|
||
#define PTBL_AMASK (PTBL_MEMSIZE - 1)
|
||
#define PTBL_M (1u << 31) /* must be sign bit */
|
||
#define PTBL_V (1u << 30)
|
||
#define PTBL_MASK (PAG_PPN | PTBL_M | PTBL_V)
|
||
|
||
/* NXM processing */
|
||
|
||
#define REF_V 0 /* ref is virt */
|
||
#define REF_P 1 /* ref is phys */
|
||
#define PF_OK 0 /* pfail ok */
|
||
#define PF_TR 1 /* pfail trap */
|
||
|
||
extern d10 *M;
|
||
extern d10 acs[AC_NBLK * AC_NUM];
|
||
extern d10 *ac_cur, *ac_prv, *last_pa;
|
||
extern a10 epta, upta;
|
||
extern int32 flags;
|
||
extern d10 pager_word;
|
||
extern int32 apr_flg;
|
||
extern d10 ebr, ubr, hsb;
|
||
extern d10 spt, cst, cstm, pur;
|
||
extern a10 dbr1, dbr2, dbr3, dbr4;
|
||
extern d10 pcst, quant;
|
||
extern t_bool paging;
|
||
extern UNIT cpu_unit;
|
||
extern jmp_buf save_env;
|
||
extern int32 test_int (void);
|
||
extern int32 pi_eval (void);
|
||
|
||
int32 eptbl[PTBL_MEMSIZE]; /* exec page table */
|
||
int32 uptbl[PTBL_MEMSIZE]; /* user page table */
|
||
int32 physptbl[PTBL_MEMSIZE]; /* phys page table */
|
||
int32 *ptbl_cur, *ptbl_prv;
|
||
int32 save_ea;
|
||
|
||
int32 ptbl_fill (a10 ea, int32 *ptbl, int32 mode);
|
||
t_stat pag_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw);
|
||
t_stat pag_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw);
|
||
t_stat pag_reset (DEVICE *dptr);
|
||
void pag_nxm (a10 pa, int32 phys, int32 trap);
|
||
|
||
/* Pager data structures
|
||
|
||
pag_dev pager device descriptor
|
||
pag_unit pager units
|
||
pager_reg pager register list
|
||
*/
|
||
|
||
UNIT pag_unit[] = {
|
||
{ UDATA (NULL, UNIT_FIX, PTBL_MEMSIZE) },
|
||
{ UDATA (NULL, UNIT_FIX, PTBL_MEMSIZE) } };
|
||
|
||
REG pag_reg[] = {
|
||
{ ORDATA (PANIC_EA, save_ea, PASIZE), REG_HRO },
|
||
{ NULL } };
|
||
|
||
DEVICE pag_dev = {
|
||
"PAG", pag_unit, pag_reg, NULL,
|
||
2, 8, PTBL_ASIZE, 1, 8, 32,
|
||
&pag_ex, &pag_dep, &pag_reset,
|
||
NULL, NULL, NULL };
|
||
|
||
/* Memory read and write routines
|
||
|
||
Read - read current or previous, read checking
|
||
ReadM - read current or previous, write checking
|
||
ReadE - read exec
|
||
ReadP - read physical
|
||
Write - write current or previous
|
||
WriteE - write exec
|
||
WriteP - write physical
|
||
AccChk - test accessibility of virtual address
|
||
*/
|
||
|
||
d10 Read (a10 ea, int32 prv)
|
||
{
|
||
register int32 pa, vpn, xpte;
|
||
|
||
if (ea < AC_NUM) return (prv? ac_prv[ea]: ac_cur[ea]); /* AC request */
|
||
vpn = PAG_GETVPN (ea); /* get page num */
|
||
xpte = prv? ptbl_prv[vpn]: ptbl_cur[vpn]; /* get exp pte */
|
||
if (xpte == 0) xpte = ptbl_fill (ea, prv? ptbl_prv: ptbl_cur, PTF_RD);
|
||
pa = PAG_XPTEPA (xpte, ea); /* calc phys addr */
|
||
if (MEM_ADDR_NXM (pa)) pag_nxm (pa, REF_V, PF_TR); /* process nxm */
|
||
return M[pa]; /* return data */
|
||
}
|
||
|
||
d10 ReadM (a10 ea, int32 prv)
|
||
{
|
||
register int32 pa, vpn, xpte;
|
||
|
||
if (ea < AC_NUM) return (prv? ac_prv[ea]: ac_cur[ea]); /* AC request */
|
||
vpn = PAG_GETVPN (ea); /* get page num */
|
||
xpte = prv? ptbl_prv[vpn]: ptbl_cur[vpn]; /* get exp pte */
|
||
if (xpte >= 0) xpte = ptbl_fill (ea, prv? ptbl_prv: ptbl_cur, PTF_WR);
|
||
pa = PAG_XPTEPA (xpte, ea); /* calc phys addr */
|
||
if (MEM_ADDR_NXM (pa)) pag_nxm (pa, REF_V, PF_TR); /* process nxm */
|
||
return M[pa]; /* return data */
|
||
}
|
||
|
||
d10 ReadE (a10 ea)
|
||
{
|
||
register int32 pa, vpn, xpte;
|
||
|
||
if (ea < AC_NUM) return AC(ea); /* AC? use current */
|
||
if (!PAGING) return M[ea]; /* phys? no mapping */
|
||
vpn = PAG_GETVPN (ea); /* get page num */
|
||
xpte = eptbl[vpn]; /* get exp pte, exec tbl */
|
||
if (xpte == 0) xpte = ptbl_fill (ea, eptbl, PTF_RD);
|
||
pa = PAG_XPTEPA (xpte, ea); /* calc phys addr */
|
||
if (MEM_ADDR_NXM (pa)) pag_nxm (pa, REF_V, PF_TR); /* process nxm */
|
||
return M[pa]; /* return data */
|
||
}
|
||
|
||
d10 ReadP (a10 ea)
|
||
{
|
||
if (ea < AC_NUM) return AC(ea); /* AC request */
|
||
if (MEM_ADDR_NXM (ea)) pag_nxm (ea, REF_P, PF_TR); /* process nxm */
|
||
return M[ea]; /* return data */
|
||
}
|
||
|
||
void Write (a10 ea, d10 val, int32 prv)
|
||
{
|
||
register int32 pa, vpn, xpte;
|
||
|
||
if (ea < AC_NUM) { /* AC request */
|
||
if (prv) ac_prv[ea] = val; /* write AC */
|
||
else ac_cur[ea] = val; }
|
||
else { vpn = PAG_GETVPN (ea); /* get page num */
|
||
xpte = prv? ptbl_prv[vpn]: ptbl_cur[vpn]; /* get exp pte */
|
||
if (xpte >= 0) xpte = ptbl_fill (ea, prv? ptbl_prv: ptbl_cur, PTF_WR);
|
||
pa = PAG_XPTEPA (xpte, ea); /* calc phys addr */
|
||
if (MEM_ADDR_NXM (pa)) pag_nxm (pa, REF_V, PF_TR); /* process nxm */
|
||
else M[pa] = val; } /* write data */
|
||
return;
|
||
}
|
||
|
||
void WriteE (a10 ea, d10 val)
|
||
{
|
||
register int32 pa, vpn, xpte;
|
||
|
||
if (ea < AC_NUM) AC(ea) = val; /* AC? use current */
|
||
else if (!PAGING) M[ea] = val; /* phys? no mapping */
|
||
else { vpn = PAG_GETVPN (ea); /* get page num */
|
||
xpte = eptbl[vpn]; /* get exp pte, exec tbl */
|
||
if (xpte >= 0) xpte = ptbl_fill (ea, eptbl, PTF_WR);
|
||
pa = PAG_XPTEPA (xpte, ea); /* calc phys addr */
|
||
if (MEM_ADDR_NXM (pa)) pag_nxm (pa, REF_V, PF_TR); /* process nxm */
|
||
else M[pa] = val; } /* write data */
|
||
return;
|
||
}
|
||
|
||
void WriteP (a10 ea, d10 val)
|
||
{
|
||
if (ea < AC_NUM) AC(ea) = val; /* AC request */
|
||
else { if (MEM_ADDR_NXM (ea)) pag_nxm (ea, REF_P, PF_TR); /* process nxm */
|
||
M[ea] = val; } /* memory */
|
||
return;
|
||
}
|
||
|
||
t_bool AccViol (a10 ea, int32 prv, int32 mode)
|
||
{
|
||
register int32 vpn, xpte;
|
||
|
||
if (ea < AC_NUM) return FALSE; /* AC request */
|
||
vpn = PAG_GETVPN (ea); /* get page num */
|
||
xpte = prv? ptbl_prv[vpn]: ptbl_cur[vpn]; /* get exp pte */
|
||
if ((xpte == 0) || ((mode & PTF_WR) && (xpte > 0))) /* not accessible? */
|
||
xpte = ptbl_fill (ea, prv? ptbl_prv: ptbl_cur, mode | PTF_MAP);
|
||
if (xpte) return FALSE; /* accessible */
|
||
return TRUE; /* not accessible */
|
||
}
|
||
|
||
void pag_nxm (a10 pa, int32 phys, int32 trap)
|
||
{
|
||
apr_flg = apr_flg | APRF_NXM; /* set APR flag */
|
||
pi_eval (); /* eval intr */
|
||
pager_word = PF_NXM | (phys? PF_NXMP: 0) |
|
||
(TSTF (F_USR)? PF_USER: 0) | ((d10) pa);
|
||
if (PAGING && trap) ABORT (PAGE_FAIL); /* trap? */
|
||
return;
|
||
}
|
||
|
||
/* Page table fill
|
||
|
||
This routine is called if the page table is invalid, or on a write
|
||
reference if the page table is read only. If the access is allowed
|
||
it stores the pte in the page table entry and returns an expanded
|
||
pte for use by the caller. Otherwise, it generates a page fail.
|
||
|
||
Notes:
|
||
- If called from the console, invalid references return a pte
|
||
of 0, and the page table entry is not filled.
|
||
- If called from MAP, invalid references return a pte of 0. The
|
||
page fail word is properly set up.
|
||
*/
|
||
|
||
#define PAGE_FAIL_TRAP if (mode & (PTF_CON | PTF_MAP)) return 0; \
|
||
ABORT (PAGE_FAIL)
|
||
#define READPT(x,y) if (MEM_ADDR_NXM (y)) { \
|
||
pag_nxm (y, REF_P, PF_OK); \
|
||
PAGE_FAIL_TRAP; } \
|
||
x = ReadP (y)
|
||
|
||
int32 ptbl_fill (a10 ea, int32 *tbl, int32 mode)
|
||
{
|
||
|
||
/* ITS paging is based on conventional page tables. ITS divides each address
|
||
space into a 128K high and low section, and uses different descriptor base
|
||
pointers (dbr) for each. ITS pages are twice the size of DEC standard;
|
||
therefore, the fill routine fills two page table entries and returns the pte
|
||
that maps the correct ITS half page. This allows the DEC paging macros to
|
||
be used in the normal path read-write routines.
|
||
|
||
ITS has no MAP instruction, therefore, physical NXM traps are ok.
|
||
*/
|
||
|
||
if (ITS) { /* ITS paging */
|
||
int32 acc, decvpn, pte, vpn, ptead, xpte;
|
||
d10 ptewd;
|
||
|
||
vpn = ITS_GETVPN (ea); /* get ITS pagno */
|
||
if (tbl == uptbl)
|
||
ptead = ((ea & RSIGN)? dbr2: dbr1) + ((vpn >> 1) & 077);
|
||
else ptead = ((ea & RSIGN)? dbr4: dbr3) + ((vpn >> 1) & 077);
|
||
ptewd = ReadP (ptead); /* get PTE pair */
|
||
pte = (int32) ((ptewd >> ((vpn & 1)? 0: 18)) & RMASK);
|
||
acc = ITS_GETACC (pte); /* get access */
|
||
pager_word = PF_VIRT | ea | ((tbl == uptbl)? PF_USER: 0) |
|
||
((mode & PTF_WR)? PF_ITS_WRITE: 0) | (acc << PF_ITS_V_ACC);
|
||
if ((acc != ITS_ACC_NO) && (!(mode & PTF_WR) || (acc == ITS_ACC_RW))) {
|
||
pte = pte & ~PTE_ITS_AGE; /* clear age */
|
||
if (vpn & 1) WriteP (ptead, (ptewd & LMASK) | pte);
|
||
else WriteP (ptead, (ptewd & RMASK) | (((d10) pte) << 18));
|
||
xpte = ((pte & PTE_ITS_PPMASK) << ITS_V_PN) | PTBL_V |
|
||
((acc == ITS_ACC_RW)? PTBL_M: 0);
|
||
decvpn = PAG_GETVPN (ea); /* get tlb idx */
|
||
if (!(mode & PTF_CON)) {
|
||
tbl[decvpn & ~1] = xpte; /* map lo ITS page */
|
||
tbl[decvpn | 1] = xpte + PAG_SIZE; } /* map hi */
|
||
return (xpte + ((decvpn & 1)? PAG_SIZE: 0)); }
|
||
PAGE_FAIL_TRAP;
|
||
} /* end ITS paging */
|
||
|
||
/* TOPS-10 paging - checked against KS10 microcode
|
||
|
||
TOPS-10 paging is also based on conventional page tables. The user page
|
||
tables are arranged contiguously at the beginning of the user process table;
|
||
however, the executive page tables are scattered through the executive and
|
||
user process tables.
|
||
*/
|
||
|
||
else if (!T20) { /* TOPS-10 paging */
|
||
int32 pte, vpn, ptead, xpte;
|
||
d10 ptewd;
|
||
|
||
vpn = PAG_GETVPN (ea); /* get virt page num */
|
||
if (tbl == uptbl) ptead = upta + UPT_T10_UMAP + (vpn >> 1);
|
||
else if (vpn < 0340) ptead = epta + EPT_T10_X000 + (vpn >> 1);
|
||
else if (vpn < 0400) ptead = upta + UPT_T10_X340 + ((vpn - 0340) >> 1);
|
||
else ptead = epta + EPT_T10_X400 + ((vpn - 0400) >> 1);
|
||
READPT (ptewd, ptead); /* get PTE pair */
|
||
pte = (int32) ((ptewd >> ((vpn & 1)? 0: 18)) & RMASK);
|
||
pager_word = PF_VIRT | ea | ((tbl == uptbl)? PF_USER: 0) |
|
||
((mode & PTF_WR)? PF_WRITE: 0) |
|
||
((pte & PTE_T10_A)? PF_T10_A |
|
||
((pte & PTE_T10_S)? PF_T10_S: 0): 0);
|
||
if (mode & PTF_MAP) pager_word = pager_word | /* map? add to pf wd */
|
||
((pte & PTE_T10_W)? PF_T10_W: 0) | /* W, S, C bits */
|
||
((pte & PTE_T10_S)? PF_T10_S: 0) |
|
||
((pte & PTE_T10_C)? PF_C: 0);
|
||
if ((pte & PTE_T10_A) && (!(mode & PTF_WR) || (pte & PTE_T10_W))) {
|
||
xpte = ((pte & PTE_PPMASK) << PAG_V_PN) | /* calc exp pte */
|
||
PTBL_V | ((pte & PTE_T10_W)? PTBL_M: 0);
|
||
if (!(mode & PTF_CON)) tbl[vpn] = xpte; /* set tbl if ~cons */
|
||
return xpte; }
|
||
PAGE_FAIL_TRAP;
|
||
} /* end TOPS10 paging */
|
||
|
||
/* TOPS-20 paging - checked against KS10 ucode.
|
||
|
||
TOPS-20 paging has three phases:
|
||
|
||
1. Starting at EPT/UPT + 540 + section number, chase section pointers to
|
||
get the pointer to the section page table. In the KS10, because there
|
||
is only one section, the microcode caches the result of this evaluation.
|
||
Also, the evaluation of indirect pointers is simplified, as the section
|
||
table index is ignored.
|
||
|
||
2. Starting with the page map pointer, chase page pointers to get the
|
||
pointer to the page. The KS10 allows the operating system to inhibit
|
||
updating of the CST (base address = 0).
|
||
|
||
3. Use the page pointer to get the CST entry. If a write reference to
|
||
a writeable page, set CST_M. If CST_M is set, set M in page table.
|
||
*/
|
||
|
||
else { /* TOPS-20 paging */
|
||
int32 pmi, vpn, xpte;
|
||
int32 flg, t;
|
||
t_bool stop;
|
||
a10 pa, csta;
|
||
d10 ptr, cste;
|
||
d10 acc = PTE_T20_W | PTE_T20_C; /* init access bits */
|
||
|
||
pager_word = PF_VIRT | ea | ((tbl == uptbl)? PF_USER: 0) |
|
||
((mode & PTF_WR)? PF_WRITE: 0); /* set page fail word */
|
||
|
||
/* First phase - evaluate section pointers - returns a ptr to a page map
|
||
As a single section machine, the KS10 short circuits this part of the
|
||
process. In particular, the indirect pointer calculation assumes that
|
||
the section table index will be 0. It adds the full pointer (not just
|
||
the right half) to the SPT base. If the section index is > 0, the
|
||
result is a physical memory address > 256KW. Depending on the size of
|
||
memory, the SPT fetch may or may not generate a NXM page fail. The
|
||
KS10 then ignores the section table index in fetching the next pointer.
|
||
|
||
The KS10 KL10 memory management diagnostic (dskec.sav) tests for this
|
||
behavior with a section index of 3. However, this would be a legal
|
||
physical address in a system with 1MW. Accordingly, the simulator
|
||
special cases non-zero section indices (which can't work in any case)
|
||
to generate the right behavior for the diagnostic.
|
||
*/
|
||
|
||
vpn = PAG_GETVPN (ea); /* get virt page num */
|
||
pa = (tbl == uptbl)? upta + UPT_T20_SCTN: epta + EPT_T20_SCTN;
|
||
READPT (ptr, pa & PAMASK); /* get section 0 ptr */
|
||
for (stop = FALSE, flg = 0; !stop; flg++) { /* eval section ptrs */
|
||
acc = acc & ptr; /* cascade acc bits */
|
||
switch (T20_GETTYP (ptr)) { /* case on ptr type */
|
||
case T20_NOA: /* no access */
|
||
default: /* undefined type */
|
||
PAGE_FAIL_TRAP; /* page fail */
|
||
case T20_IMM: /* immediate */
|
||
stop = TRUE; /* exit */
|
||
break;
|
||
case T20_SHR: /* shared */
|
||
pa = (int32) (spt + (ptr & RMASK)); /* get SPT idx */
|
||
READPT (ptr, pa & PAMASK); /* get SPT entry */
|
||
stop = TRUE; /* exit */
|
||
break;
|
||
case T20_IND: /* indirect */
|
||
if (flg && (t = test_int ())) ABORT (t);
|
||
pmi = T20_GETPMI (ptr); /* get sect tbl idx */
|
||
pa = (int32) (spt + (ptr & RMASK)); /* get SPT idx */
|
||
if (pmi) { /* for dskec */
|
||
pag_nxm ((pmi << 18) | pa, REF_P, PF_OK);
|
||
PAGE_FAIL_TRAP; }
|
||
READPT (ptr, pa & PAMASK); /* get SPT entry */
|
||
if (ptr & PTE_T20_STM) { PAGE_FAIL_TRAP; }
|
||
pa = PAG_PTEPA (ptr, pmi); /* index off page */
|
||
READPT (ptr, pa & PAMASK); /* get pointer */
|
||
break; /* continue in loop */
|
||
} /* end case */
|
||
} /* end for */
|
||
|
||
/* Second phase - found page map ptr, evaluate page pointers */
|
||
|
||
pa = PAG_PTEPA (ptr, vpn); /* get ptbl address */
|
||
for (stop = FALSE, flg = 0; !stop; flg++) { /* eval page ptrs */
|
||
if (ptr & PTE_T20_STM) { PAGE_FAIL_TRAP; } /* non-res? */
|
||
if (cst) { /* cst really there? */
|
||
csta = (int32) ((cst + (ptr & PTE_PPMASK)) & PAMASK);
|
||
READPT (cste, csta); /* get CST entry */
|
||
if ((cste & CST_AGE) == 0) { PAGE_FAIL_TRAP; }
|
||
cste = (cste & cstm) | pur; /* update entry */
|
||
WriteP (csta, cste); } /* rewrite */
|
||
READPT (ptr, pa & PAMASK); /* get pointer */
|
||
acc = acc & ptr; /* cascade acc bits */
|
||
switch (T20_GETTYP (ptr)) { /* case on ptr type */
|
||
case T20_NOA: /* no access */
|
||
default: /* undefined type */
|
||
PAGE_FAIL_TRAP; /* page fail */
|
||
case T20_IMM: /* immediate */
|
||
stop = TRUE; /* exit */
|
||
break;
|
||
case T20_SHR: /* shared */
|
||
pa = (int32) (spt + (ptr & RMASK)); /* get SPT idx */
|
||
READPT (ptr, pa & PAMASK); /* get SPT entry */
|
||
stop = TRUE; /* exit */
|
||
break;
|
||
case T20_IND: /* indirect */
|
||
if (flg && (t = test_int ())) ABORT (t);
|
||
pmi = T20_GETPMI (ptr); /* get section index */
|
||
pa = (int32) (spt + (ptr & RMASK)); /* get SPT idx */
|
||
READPT (ptr, pa & PAMASK); /* get SPT entry */
|
||
pa = PAG_PTEPA (ptr, pmi); /* index off page */
|
||
break; /* continue in loop */
|
||
} /* end case */
|
||
} /* end for */
|
||
|
||
/* Last phase - have final page pointer, check modifiability */
|
||
|
||
if (ptr & PTE_T20_STM) { PAGE_FAIL_TRAP; } /* non-resident? */
|
||
if (cst) { /* CST really there? */
|
||
csta = (int32) ((cst + (ptr & PTE_PPMASK)) & PAMASK);
|
||
READPT (cste, csta); /* get CST entry */
|
||
if ((cste & CST_AGE) == 0) { PAGE_FAIL_TRAP; }
|
||
cste = (cste & cstm) | pur; } /* update entry */
|
||
else cste = 0; /* no, entry = 0 */
|
||
pager_word = pager_word | PF_T20_DN; /* set eval done */
|
||
xpte = ((int32) ((ptr & PTE_PPMASK) << PAG_V_PN)) | PTBL_V;
|
||
if (mode & PTF_WR) { /* write? */
|
||
if (acc & PTE_T20_W) { /* writable? */
|
||
xpte = xpte | PTBL_M; /* set PTE M */
|
||
cste = cste | CST_M; } /* set CST M */
|
||
else { PAGE_FAIL_TRAP; } } /* no, trap */
|
||
if (cst) WriteP (csta, cste); /* write CST entry */
|
||
if (mode & PTF_MAP) pager_word = pager_word | /* map? more in pf wd */
|
||
((xpte & PTBL_M)? PF_T20_M: 0) | /* M, W, C bits */
|
||
((acc & PTE_T20_W)? PF_T20_W: 0) |
|
||
((acc & PTE_T20_C)? PF_C: 0);
|
||
if (!(mode & PTF_CON)) tbl[vpn] = xpte; /* set tbl if ~cons */
|
||
return xpte;
|
||
} /* end TOPS20 paging */
|
||
}
|
||
|
||
/* Set up pointers for AC, memory, and process table access */
|
||
|
||
void set_dyn_ptrs (void)
|
||
{
|
||
int32 t;
|
||
|
||
if (PAGING) {
|
||
ac_cur = &acs[UBR_GETCURAC (ubr) * AC_NUM];
|
||
ac_prv = &acs[UBR_GETPRVAC (ubr) * AC_NUM];
|
||
if (TSTF (F_USR)) ptbl_cur = ptbl_prv = &uptbl[0];
|
||
else { ptbl_cur = &eptbl[0];
|
||
ptbl_prv = TSTF (F_UIO)? &uptbl[0]: &eptbl[0]; } }
|
||
else { ac_cur = ac_prv = &acs[0];
|
||
ptbl_cur = ptbl_prv = &physptbl[0]; }
|
||
t = EBR_GETEBR (ebr);
|
||
epta = t << PAG_V_PN;
|
||
if (ITS) upta = (int32) ubr & PAMASK;
|
||
else { t = UBR_GETUBR (ubr);
|
||
upta = t << PAG_V_PN; }
|
||
return;
|
||
}
|
||
|
||
/* MAP instruction, TOPS-10 and TOPS-20 only
|
||
|
||
According to the KS-10 ucode, map with paging disabled sets
|
||
"accessible, writeable, software", regardless of whether
|
||
TOPS-10 or TOPS-20 paging is implemented
|
||
*/
|
||
|
||
d10 map (a10 ea, int32 prv)
|
||
{
|
||
int32 xpte;
|
||
d10 val = (TSTF (F_USR)? PF_USER: 0);
|
||
|
||
if (!PAGING) return (val | PF_T10_A | PF_T10_W | PF_T10_S | ea);
|
||
xpte = ptbl_fill (ea, prv? ptbl_prv: ptbl_cur, PTF_MAP); /* get exp pte */
|
||
if (xpte) val = (pager_word & ~PAMASK) | PAG_XPTEPA (xpte, ea);
|
||
else { if (pager_word & PF_HARD) val = pager_word; /* hard error */
|
||
else val = val | PF_VIRT | ea; } /* inaccessible */
|
||
return val;
|
||
}
|
||
|
||
/* Mapping routine for console */
|
||
|
||
a10 conmap (a10 ea, int32 mode, int32 sw)
|
||
{
|
||
int32 xpte, *tbl;
|
||
|
||
if (!PAGING) return ea;
|
||
set_dyn_ptrs (); /* in case changed */
|
||
if (sw & SWMASK ('E')) tbl = eptbl;
|
||
else if (sw & SWMASK ('U')) tbl = uptbl;
|
||
else tbl = ptbl_cur;
|
||
xpte = ptbl_fill (ea, tbl, mode);
|
||
if (xpte) return PAG_XPTEPA (xpte, ea);
|
||
else return MAXMEMSIZE;
|
||
}
|
||
|
||
/* Common pager instructions */
|
||
|
||
t_bool clrpt (a10 ea, int32 prv)
|
||
{
|
||
int32 vpn = PAG_GETVPN (ea); /* get page num */
|
||
|
||
if (ITS) { /* ITS? */
|
||
uptbl[vpn & ~1] = 0; /* clear double size */
|
||
uptbl[vpn | 1] = 0; /* entries in */
|
||
eptbl[vpn & ~1] = 0; /* both page tables */
|
||
eptbl[vpn | 1] = 0; }
|
||
else { uptbl[vpn] = 0; /* clear entries in */
|
||
eptbl[vpn] = 0; } /* both page tables */
|
||
return FALSE;
|
||
}
|
||
|
||
t_bool wrebr (a10 ea, int32 prv)
|
||
{
|
||
ebr = ea & EBR_MASK; /* store EBR */
|
||
pag_reset (&pag_dev); /* clear page tables */
|
||
set_dyn_ptrs (); /* set dynamic ptrs */
|
||
return FALSE;
|
||
}
|
||
|
||
t_bool rdebr (a10 ea, int32 prv)
|
||
{
|
||
Write (ea, (ebr & EBR_MASK), prv);
|
||
return FALSE;
|
||
}
|
||
|
||
t_bool wrubr (a10 ea, int32 prv)
|
||
{
|
||
d10 val = Read (ea, prv);
|
||
d10 ubr_mask = (ITS)? PAMASK: UBR_UBRMASK; /* ITS: ubr is wd addr */
|
||
|
||
if (val & UBR_SETACB) ubr = ubr & ~UBR_ACBMASK; /* set AC's? */
|
||
else val = val & ~UBR_ACBMASK; /* no, keep old val */
|
||
if (val & UBR_SETUBR) { /* set UBR? */
|
||
ubr = ubr & ~ubr_mask;
|
||
pag_reset (&pag_dev); } /* yes, clr pg tbls */
|
||
else val = val & ~ubr_mask; /* no, keep old val */
|
||
ubr = (ubr | val) & (UBR_ACBMASK | ubr_mask);
|
||
set_dyn_ptrs ();
|
||
return FALSE;
|
||
}
|
||
|
||
t_bool rdubr (a10 ea, int32 prv)
|
||
{
|
||
ubr = ubr & (UBR_ACBMASK | (ITS? PAMASK: UBR_UBRMASK));
|
||
Write (ea, UBRWORD, prv);
|
||
return FALSE;
|
||
}
|
||
|
||
t_bool wrhsb (a10 ea, int32 prv)
|
||
{
|
||
hsb = Read (ea, prv) & PAMASK;
|
||
return FALSE;
|
||
}
|
||
|
||
t_bool rdhsb (a10 ea, int32 prv)
|
||
{
|
||
Write (ea, hsb, prv);
|
||
return FALSE;
|
||
}
|
||
|
||
/* TOPS20 pager instructions */
|
||
|
||
t_bool wrspb (a10 ea, int32 prv)
|
||
{
|
||
spt = Read (ea, prv);
|
||
return FALSE;
|
||
}
|
||
|
||
t_bool rdspb (a10 ea, int32 prv)
|
||
{
|
||
Write (ea, spt, prv);
|
||
return FALSE;
|
||
}
|
||
|
||
t_bool wrcsb (a10 ea, int32 prv)
|
||
{
|
||
cst = Read (ea, prv);
|
||
return FALSE;
|
||
}
|
||
|
||
t_bool rdcsb (a10 ea, int32 prv)
|
||
{
|
||
Write (ea, cst, prv);
|
||
return FALSE;
|
||
}
|
||
|
||
t_bool wrpur (a10 ea, int32 prv)
|
||
{
|
||
pur = Read (ea, prv);
|
||
return FALSE;
|
||
}
|
||
|
||
t_bool rdpur (a10 ea, int32 prv)
|
||
{
|
||
Write (ea, pur, prv);
|
||
return FALSE;
|
||
}
|
||
|
||
t_bool wrcstm (a10 ea, int32 prv)
|
||
{
|
||
cstm = Read (ea, prv);
|
||
return FALSE;
|
||
}
|
||
|
||
t_bool rdcstm (a10 ea, int32 prv)
|
||
{
|
||
Write (ea, cstm, prv);
|
||
return FALSE;
|
||
}
|
||
|
||
/* ITS pager instructions
|
||
The KS10 does not implement the JPC option.
|
||
*/
|
||
|
||
t_bool clrcsh (a10 ea, int32 prv)
|
||
{
|
||
return FALSE;
|
||
}
|
||
|
||
t_bool ldbr1 (a10 ea, int32 prv)
|
||
{
|
||
dbr1 = ea;
|
||
pag_reset (&pag_dev);
|
||
return FALSE;
|
||
}
|
||
|
||
t_bool sdbr1 (a10 ea, int32 prv)
|
||
{
|
||
Write (ea, dbr1, prv);
|
||
return FALSE;
|
||
}
|
||
|
||
t_bool ldbr2 (a10 ea, int32 prv)
|
||
{
|
||
dbr2 = ea;
|
||
pag_reset (&pag_dev);
|
||
return FALSE;
|
||
}
|
||
|
||
t_bool sdbr2 (a10 ea, int32 prv)
|
||
{
|
||
Write (ea, dbr2, prv);
|
||
return FALSE;
|
||
}
|
||
|
||
t_bool ldbr3 (a10 ea, int32 prv)
|
||
{
|
||
dbr3 = ea;
|
||
pag_reset (&pag_dev);
|
||
return FALSE;
|
||
}
|
||
|
||
t_bool sdbr3 (a10 ea, int32 prv)
|
||
{
|
||
Write (ea, dbr3, prv);
|
||
return FALSE;
|
||
}
|
||
|
||
t_bool ldbr4 (a10 ea, int32 prv)
|
||
{
|
||
dbr4 = ea;
|
||
pag_reset (&pag_dev);
|
||
return FALSE;
|
||
}
|
||
|
||
t_bool sdbr4 (a10 ea, int32 prv)
|
||
{
|
||
Write (ea, dbr4, prv);
|
||
return FALSE;
|
||
}
|
||
|
||
t_bool wrpcst (a10 ea, int32 prv)
|
||
{
|
||
pcst = Read (ea, prv);
|
||
return FALSE;
|
||
}
|
||
|
||
t_bool rdpcst (a10 ea, int32 prv)
|
||
{
|
||
Write (ea, pcst, prv);
|
||
return FALSE;
|
||
}
|
||
|
||
t_bool lpmr (a10 ea, int32 prv)
|
||
{
|
||
d10 val;
|
||
|
||
val = Read (ADDA (ea, 2), prv);
|
||
dbr1 = (a10) (Read (ea, prv) & AMASK);
|
||
dbr2 = (a10) (Read (ADDA (ea, 1), prv) & AMASK);
|
||
quant = val;
|
||
return FALSE;
|
||
}
|
||
|
||
t_bool spm (a10 ea, int32 prv)
|
||
{
|
||
|
||
ReadM (ADDA (ea, 2), prv);
|
||
Write (ea, dbr1, prv);
|
||
Write (ADDA (ea, 1), dbr2, prv);
|
||
Write (ADDA (ea, 2), quant, prv);
|
||
return FALSE;
|
||
}
|
||
|
||
t_stat pag_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw)
|
||
{
|
||
int32 tbln = uptr - pag_unit;
|
||
|
||
if (addr >= PTBL_MEMSIZE) return SCPE_NXM;
|
||
*vptr = tbln? uptbl[addr]: eptbl[addr];;
|
||
return SCPE_OK;
|
||
}
|
||
|
||
t_stat pag_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw)
|
||
{
|
||
int32 tbln = uptr - pag_unit;
|
||
|
||
if (addr >= PTBL_MEMSIZE) return SCPE_NXM;
|
||
if (tbln) uptbl[addr] = (int32) val & PTBL_MASK;
|
||
else eptbl[addr] = (int32) val & PTBL_MASK;
|
||
return SCPE_OK;
|
||
}
|
||
|
||
t_stat pag_reset (DEVICE *dptr)
|
||
{
|
||
int32 i;
|
||
|
||
for (i = 0; i < PTBL_MEMSIZE; i++) {
|
||
eptbl[i] = uptbl[i] = 0;
|
||
physptbl[i] = (i << PAG_V_PN) + PTBL_M + PTBL_V; }
|
||
return SCPE_OK;
|
||
}
|