| 1 | /* pdp10_pag.c: PDP-10 paging subsystem simulator\r |
| 2 | \r |
| 3 | Copyright (c) 1993-2005, Robert M Supnik\r |
| 4 | \r |
| 5 | Permission is hereby granted, free of charge, to any person obtaining a\r |
| 6 | copy of this software and associated documentation files (the "Software"),\r |
| 7 | to deal in the Software without restriction, including without limitation\r |
| 8 | the rights to use, copy, modify, merge, publish, distribute, sublicense,\r |
| 9 | and/or sell copies of the Software, and to permit persons to whom the\r |
| 10 | Software is furnished to do so, subject to the following conditions:\r |
| 11 | \r |
| 12 | The above copyright notice and this permission notice shall be included in\r |
| 13 | all copies or substantial portions of the Software.\r |
| 14 | \r |
| 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r |
| 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r |
| 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\r |
| 18 | ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\r |
| 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r |
| 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r |
| 21 | \r |
| 22 | Except as contained in this notice, the name of Robert M Supnik shall not be\r |
| 23 | used in advertising or otherwise to promote the sale, use or other dealings\r |
| 24 | in this Software without prior written authorization from Robert M Supnik.\r |
| 25 | \r |
| 26 | pag KS10 pager\r |
| 27 | \r |
| 28 | 22-Sep-05 RMS Fixed declarations (from Sterling Garwood)\r |
| 29 | 02-Dec-01 RMS Fixed bug in ITS LPMR (found by Dave Conroy)\r |
| 30 | 21-Aug-01 RMS Fixed bug in ITS paging (found by Miriam Lennox)\r |
| 31 | Removed register from declarations\r |
| 32 | 19-May-01 RMS Added workaround for TOPS-20 V4.1 boot bug\r |
| 33 | 03-May-01 RMS Fixed bug in indirect page table pointer processing\r |
| 34 | 29-Apr-01 RMS Added CLRCSH for ITS, fixed LPMR\r |
| 35 | \r |
| 36 | The pager consists of a standard hardware part (the translation\r |
| 37 | tables) and an operating-system specific page table fill routine.\r |
| 38 | \r |
| 39 | There are two translation tables, one for executive mode and one\r |
| 40 | for user mode. Each table consists of 512 page table entries,\r |
| 41 | one for each page in the 18b virtual address space. Each pte\r |
| 42 | contains (in the hardware) a valid bit, a writeable bit, an\r |
| 43 | address space bit (executive or user), and a cacheable bit, plus\r |
| 44 | the physical page number corresponding to the virtual page. In\r |
| 45 | the simulator, the pte is expanded for rapid processing of normal\r |
| 46 | reads and writes. An expanded pte contains a valid bit, a writeable\r |
| 47 | bit, and the physical page number shifted left by the page size.\r |
| 48 | \r |
| 49 | Expanded pte meaning\r |
| 50 | 0 invalid\r |
| 51 | >0 read only\r |
| 52 | <0 read write\r |
| 53 | \r |
| 54 | There is a third, physical table, which is used in place of the\r |
| 55 | executive and user tables if paging is off. Its entries are always\r |
| 56 | valid and always writeable.\r |
| 57 | \r |
| 58 | To translate a virtual to physical address, the simulator uses\r |
| 59 | the virtual page number to index into the appropriate page table.\r |
| 60 | If the page table entry (pte) is not valid, the page fill routine\r |
| 61 | is called to see if the entry is merely not filled or is truly\r |
| 62 | inaccessible. If the pte is valid but not writeable, and the\r |
| 63 | reference is a write reference, the page fill routine is also\r |
| 64 | called to see if the reference can be resolved.\r |
| 65 | \r |
| 66 | The page fill routine is operating system dependent. Three styles\r |
| 67 | of paging are supported:\r |
| 68 | \r |
| 69 | TOPS10 known in the KS10 microcode as KI10 paging,\r |
| 70 | used by earlier versions of TOPS10\r |
| 71 | TOPS20 known in the KS10 microcode as KL10 paging,\r |
| 72 | used by later versions of TOPS10, and TOPS20\r |
| 73 | ITS used only by ITS\r |
| 74 | \r |
| 75 | TOPS10 vs TOPS20 is selected by a bit in the EBR; ITS paging is\r |
| 76 | "hardwired" (it required different microcode).\r |
| 77 | */\r |
| 78 | \r |
| 79 | #include "pdp10_defs.h"\r |
| 80 | #include <setjmp.h>\r |
| 81 | \r |
| 82 | /* Page table (contains expanded pte's) */\r |
| 83 | \r |
| 84 | #define PTBL_ASIZE PAG_N_VPN\r |
| 85 | #define PTBL_MEMSIZE (1 << PTBL_ASIZE) /* page table size */\r |
| 86 | #define PTBL_AMASK (PTBL_MEMSIZE - 1)\r |
| 87 | #define PTBL_M (1u << 31) /* must be sign bit */\r |
| 88 | #define PTBL_V (1u << 30)\r |
| 89 | #define PTBL_MASK (PAG_PPN | PTBL_M | PTBL_V)\r |
| 90 | \r |
| 91 | /* NXM processing */\r |
| 92 | \r |
| 93 | #define REF_V 0 /* ref is virt */\r |
| 94 | #define REF_P 1 /* ref is phys */\r |
| 95 | #define PF_OK 0 /* pfail ok */\r |
| 96 | #define PF_TR 1 /* pfail trap */\r |
| 97 | \r |
| 98 | extern d10 *M;\r |
| 99 | extern d10 acs[AC_NBLK * AC_NUM];\r |
| 100 | extern d10 *ac_cur, *ac_prv, *last_pa;\r |
| 101 | extern a10 epta, upta;\r |
| 102 | extern int32 flags;\r |
| 103 | extern d10 pager_word;\r |
| 104 | extern int32 apr_flg;\r |
| 105 | extern d10 ebr, ubr, hsb;\r |
| 106 | extern d10 spt, cst, cstm, pur;\r |
| 107 | extern a10 dbr1, dbr2, dbr3, dbr4;\r |
| 108 | extern d10 pcst, quant;\r |
| 109 | extern t_bool paging;\r |
| 110 | extern UNIT cpu_unit;\r |
| 111 | extern jmp_buf save_env;\r |
| 112 | extern int32 test_int (void);\r |
| 113 | extern int32 pi_eval (void);\r |
| 114 | \r |
| 115 | int32 eptbl[PTBL_MEMSIZE]; /* exec page table */\r |
| 116 | int32 uptbl[PTBL_MEMSIZE]; /* user page table */\r |
| 117 | int32 physptbl[PTBL_MEMSIZE]; /* phys page table */\r |
| 118 | int32 *ptbl_cur, *ptbl_prv;\r |
| 119 | int32 save_ea;\r |
| 120 | \r |
| 121 | int32 ptbl_fill (a10 ea, int32 *ptbl, int32 mode);\r |
| 122 | t_stat pag_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw);\r |
| 123 | t_stat pag_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw);\r |
| 124 | t_stat pag_reset (DEVICE *dptr);\r |
| 125 | void pag_nxm (a10 pa, int32 phys, int32 trap);\r |
| 126 | \r |
| 127 | /* Pager data structures\r |
| 128 | \r |
| 129 | pag_dev pager device descriptor\r |
| 130 | pag_unit pager units\r |
| 131 | pager_reg pager register list\r |
| 132 | */\r |
| 133 | \r |
| 134 | UNIT pag_unit[] = {\r |
| 135 | { UDATA (NULL, UNIT_FIX, PTBL_MEMSIZE) },\r |
| 136 | { UDATA (NULL, UNIT_FIX, PTBL_MEMSIZE) }\r |
| 137 | };\r |
| 138 | \r |
| 139 | REG pag_reg[] = {\r |
| 140 | { ORDATA (PANIC_EA, save_ea, PASIZE), REG_HRO },\r |
| 141 | { NULL }\r |
| 142 | };\r |
| 143 | \r |
| 144 | DEVICE pag_dev = {\r |
| 145 | "PAG", pag_unit, pag_reg, NULL,\r |
| 146 | 2, 8, PTBL_ASIZE, 1, 8, 32,\r |
| 147 | &pag_ex, &pag_dep, &pag_reset,\r |
| 148 | NULL, NULL, NULL,\r |
| 149 | NULL, 0\r |
| 150 | };\r |
| 151 | \r |
| 152 | /* Memory read and write routines\r |
| 153 | \r |
| 154 | Read - read current or previous, read checking\r |
| 155 | ReadM - read current or previous, write checking\r |
| 156 | ReadE - read exec\r |
| 157 | ReadP - read physical\r |
| 158 | Write - write current or previous\r |
| 159 | WriteE - write exec\r |
| 160 | WriteP - write physical\r |
| 161 | AccChk - test accessibility of virtual address\r |
| 162 | */\r |
| 163 | \r |
| 164 | d10 Read (a10 ea, int32 prv)\r |
| 165 | {\r |
| 166 | int32 pa, vpn, xpte;\r |
| 167 | \r |
| 168 | if (ea < AC_NUM) return (prv? ac_prv[ea]: ac_cur[ea]); /* AC request */\r |
| 169 | vpn = PAG_GETVPN (ea); /* get page num */\r |
| 170 | xpte = prv? ptbl_prv[vpn]: ptbl_cur[vpn]; /* get exp pte */\r |
| 171 | if (xpte == 0) xpte = ptbl_fill (ea, prv? ptbl_prv: ptbl_cur, PTF_RD);\r |
| 172 | pa = PAG_XPTEPA (xpte, ea); /* calc phys addr */\r |
| 173 | if (MEM_ADDR_NXM (pa)) pag_nxm (pa, REF_V, PF_TR); /* process nxm */\r |
| 174 | return M[pa]; /* return data */\r |
| 175 | }\r |
| 176 | \r |
| 177 | d10 ReadM (a10 ea, int32 prv)\r |
| 178 | {\r |
| 179 | int32 pa, vpn, xpte;\r |
| 180 | \r |
| 181 | if (ea < AC_NUM) return (prv? ac_prv[ea]: ac_cur[ea]); /* AC request */\r |
| 182 | vpn = PAG_GETVPN (ea); /* get page num */\r |
| 183 | xpte = prv? ptbl_prv[vpn]: ptbl_cur[vpn]; /* get exp pte */\r |
| 184 | if (xpte >= 0) xpte = ptbl_fill (ea, prv? ptbl_prv: ptbl_cur, PTF_WR);\r |
| 185 | pa = PAG_XPTEPA (xpte, ea); /* calc phys addr */\r |
| 186 | if (MEM_ADDR_NXM (pa)) pag_nxm (pa, REF_V, PF_TR); /* process nxm */\r |
| 187 | return M[pa]; /* return data */\r |
| 188 | }\r |
| 189 | \r |
| 190 | d10 ReadE (a10 ea)\r |
| 191 | {\r |
| 192 | int32 pa, vpn, xpte;\r |
| 193 | \r |
| 194 | if (ea < AC_NUM) return AC(ea); /* AC? use current */\r |
| 195 | if (!PAGING) return M[ea]; /* phys? no mapping */\r |
| 196 | vpn = PAG_GETVPN (ea); /* get page num */\r |
| 197 | xpte = eptbl[vpn]; /* get exp pte, exec tbl */\r |
| 198 | if (xpte == 0) xpte = ptbl_fill (ea, eptbl, PTF_RD);\r |
| 199 | pa = PAG_XPTEPA (xpte, ea); /* calc phys addr */\r |
| 200 | if (MEM_ADDR_NXM (pa)) pag_nxm (pa, REF_V, PF_TR); /* process nxm */\r |
| 201 | return M[pa]; /* return data */\r |
| 202 | }\r |
| 203 | \r |
| 204 | d10 ReadP (a10 ea)\r |
| 205 | {\r |
| 206 | if (ea < AC_NUM) return AC(ea); /* AC request */\r |
| 207 | if (MEM_ADDR_NXM (ea)) pag_nxm (ea, REF_P, PF_TR); /* process nxm */\r |
| 208 | return M[ea]; /* return data */\r |
| 209 | }\r |
| 210 | \r |
| 211 | void Write (a10 ea, d10 val, int32 prv)\r |
| 212 | {\r |
| 213 | int32 pa, vpn, xpte;\r |
| 214 | \r |
| 215 | if (ea < AC_NUM) { /* AC request */\r |
| 216 | if (prv) ac_prv[ea] = val; /* write AC */\r |
| 217 | else ac_cur[ea] = val;\r |
| 218 | }\r |
| 219 | else {\r |
| 220 | vpn = PAG_GETVPN (ea); /* get page num */\r |
| 221 | xpte = prv? ptbl_prv[vpn]: ptbl_cur[vpn]; /* get exp pte */\r |
| 222 | if (xpte >= 0) xpte = ptbl_fill (ea, prv? ptbl_prv: ptbl_cur, PTF_WR);\r |
| 223 | pa = PAG_XPTEPA (xpte, ea); /* calc phys addr */\r |
| 224 | if (MEM_ADDR_NXM (pa)) pag_nxm (pa, REF_V, PF_TR); /* process nxm */\r |
| 225 | else M[pa] = val; /* write data */\r |
| 226 | }\r |
| 227 | return;\r |
| 228 | }\r |
| 229 | \r |
| 230 | void WriteE (a10 ea, d10 val)\r |
| 231 | {\r |
| 232 | int32 pa, vpn, xpte;\r |
| 233 | \r |
| 234 | if (ea < AC_NUM) AC(ea) = val; /* AC? use current */\r |
| 235 | else if (!PAGING) M[ea] = val; /* phys? no mapping */\r |
| 236 | else {\r |
| 237 | vpn = PAG_GETVPN (ea); /* get page num */\r |
| 238 | xpte = eptbl[vpn]; /* get exp pte, exec tbl */\r |
| 239 | if (xpte >= 0) xpte = ptbl_fill (ea, eptbl, PTF_WR);\r |
| 240 | pa = PAG_XPTEPA (xpte, ea); /* calc phys addr */\r |
| 241 | if (MEM_ADDR_NXM (pa)) pag_nxm (pa, REF_V, PF_TR); /* process nxm */\r |
| 242 | else M[pa] = val; /* write data */\r |
| 243 | }\r |
| 244 | return;\r |
| 245 | }\r |
| 246 | \r |
| 247 | void WriteP (a10 ea, d10 val)\r |
| 248 | {\r |
| 249 | if (ea < AC_NUM) AC(ea) = val; /* AC request */\r |
| 250 | else {\r |
| 251 | if (MEM_ADDR_NXM (ea)) pag_nxm (ea, REF_P, PF_TR); /* process nxm */\r |
| 252 | M[ea] = val; /* memory */\r |
| 253 | }\r |
| 254 | return;\r |
| 255 | }\r |
| 256 | \r |
| 257 | t_bool AccViol (a10 ea, int32 prv, int32 mode)\r |
| 258 | {\r |
| 259 | int32 vpn, xpte;\r |
| 260 | \r |
| 261 | if (ea < AC_NUM) return FALSE; /* AC request */\r |
| 262 | vpn = PAG_GETVPN (ea); /* get page num */\r |
| 263 | xpte = prv? ptbl_prv[vpn]: ptbl_cur[vpn]; /* get exp pte */\r |
| 264 | if ((xpte == 0) || ((mode & PTF_WR) && (xpte > 0))) /* not accessible? */\r |
| 265 | xpte = ptbl_fill (ea, prv? ptbl_prv: ptbl_cur, mode | PTF_MAP);\r |
| 266 | if (xpte) return FALSE; /* accessible */\r |
| 267 | return TRUE; /* not accessible */\r |
| 268 | }\r |
| 269 | \r |
| 270 | void pag_nxm (a10 pa, int32 phys, int32 trap)\r |
| 271 | {\r |
| 272 | apr_flg = apr_flg | APRF_NXM; /* set APR flag */\r |
| 273 | pi_eval (); /* eval intr */\r |
| 274 | pager_word = PF_NXM | (phys? PF_NXMP: 0) |\r |
| 275 | (TSTF (F_USR)? PF_USER: 0) | ((d10) pa);\r |
| 276 | if (PAGING && trap) ABORT (PAGE_FAIL); /* trap? */\r |
| 277 | return;\r |
| 278 | }\r |
| 279 | \r |
| 280 | /* Page table fill\r |
| 281 | \r |
| 282 | This routine is called if the page table is invalid, or on a write\r |
| 283 | reference if the page table is read only. If the access is allowed\r |
| 284 | it stores the pte in the page table entry and returns an expanded\r |
| 285 | pte for use by the caller. Otherwise, it generates a page fail.\r |
| 286 | \r |
| 287 | Notes:\r |
| 288 | - If called from the console, invalid references return a pte\r |
| 289 | of 0, and the page table entry is not filled.\r |
| 290 | - If called from MAP, invalid references return a pte of 0. The\r |
| 291 | page fail word is properly set up.\r |
| 292 | */\r |
| 293 | \r |
| 294 | #define PAGE_FAIL_TRAP if (mode & (PTF_CON | PTF_MAP)) return 0; \\r |
| 295 | ABORT (PAGE_FAIL)\r |
| 296 | #define READPT(x,y) if (MEM_ADDR_NXM (y)) { \\r |
| 297 | pag_nxm (y, REF_P, PF_OK); \\r |
| 298 | PAGE_FAIL_TRAP; \\r |
| 299 | } \\r |
| 300 | x = ReadP (y)\r |
| 301 | \r |
| 302 | int32 ptbl_fill (a10 ea, int32 *tbl, int32 mode)\r |
| 303 | {\r |
| 304 | \r |
| 305 | /* ITS paging is based on conventional page tables. ITS divides each address\r |
| 306 | space into a 128K high and low section, and uses different descriptor base\r |
| 307 | pointers (dbr) for each. ITS pages are twice the size of DEC standard;\r |
| 308 | therefore, the fill routine fills two page table entries and returns the pte\r |
| 309 | that maps the correct ITS half page. This allows the DEC paging macros to\r |
| 310 | be used in the normal path read-write routines.\r |
| 311 | \r |
| 312 | ITS has no MAP instruction, therefore, physical NXM traps are ok.\r |
| 313 | */\r |
| 314 | \r |
| 315 | if (Q_ITS) { /* ITS paging */\r |
| 316 | int32 acc, decvpn, pte, vpn, ptead, xpte;\r |
| 317 | d10 ptewd;\r |
| 318 | \r |
| 319 | vpn = ITS_GETVPN (ea); /* get ITS pagno */\r |
| 320 | if (tbl == uptbl)\r |
| 321 | ptead = ((ea & RSIGN)? dbr2: dbr1) + ((vpn >> 1) & 077);\r |
| 322 | else ptead = ((ea & RSIGN)? dbr3: dbr4) + ((vpn >> 1) & 077);\r |
| 323 | ptewd = ReadP (ptead); /* get PTE pair */\r |
| 324 | pte = (int32) ((ptewd >> ((vpn & 1)? 0: 18)) & RMASK);\r |
| 325 | acc = ITS_GETACC (pte); /* get access */\r |
| 326 | pager_word = PF_VIRT | ea | ((tbl == uptbl)? PF_USER: 0) |\r |
| 327 | ((mode & PTF_WR)? PF_ITS_WRITE: 0) | (acc << PF_ITS_V_ACC);\r |
| 328 | if ((acc != ITS_ACC_NO) && (!(mode & PTF_WR) || (acc == ITS_ACC_RW))) { \r |
| 329 | pte = pte & ~PTE_ITS_AGE; /* clear age */\r |
| 330 | if (vpn & 1) WriteP (ptead, (ptewd & LMASK) | pte);\r |
| 331 | else WriteP (ptead, (ptewd & RMASK) | (((d10) pte) << 18));\r |
| 332 | xpte = ((pte & PTE_ITS_PPMASK) << ITS_V_PN) | PTBL_V |\r |
| 333 | ((acc == ITS_ACC_RW)? PTBL_M: 0);\r |
| 334 | decvpn = PAG_GETVPN (ea); /* get tlb idx */\r |
| 335 | if (!(mode & PTF_CON)) {\r |
| 336 | tbl[decvpn & ~1] = xpte; /* map lo ITS page */\r |
| 337 | tbl[decvpn | 1] = xpte + PAG_SIZE; /* map hi */\r |
| 338 | }\r |
| 339 | return (xpte + ((decvpn & 1)? PAG_SIZE: 0));\r |
| 340 | }\r |
| 341 | PAGE_FAIL_TRAP;\r |
| 342 | } /* end ITS paging */\r |
| 343 | \r |
| 344 | /* TOPS-10 paging - checked against KS10 microcode\r |
| 345 | \r |
| 346 | TOPS-10 paging is also based on conventional page tables. The user page\r |
| 347 | tables are arranged contiguously at the beginning of the user process table;\r |
| 348 | however, the executive page tables are scattered through the executive and\r |
| 349 | user process tables.\r |
| 350 | */\r |
| 351 | \r |
| 352 | else if (!T20PAG) { /* TOPS-10 paging */\r |
| 353 | int32 pte, vpn, ptead, xpte;\r |
| 354 | d10 ptewd;\r |
| 355 | \r |
| 356 | vpn = PAG_GETVPN (ea); /* get virt page num */\r |
| 357 | if (tbl == uptbl) ptead = upta + UPT_T10_UMAP + (vpn >> 1);\r |
| 358 | else if (vpn < 0340) ptead = epta + EPT_T10_X000 + (vpn >> 1);\r |
| 359 | else if (vpn < 0400) ptead = upta + UPT_T10_X340 + ((vpn - 0340) >> 1);\r |
| 360 | else ptead = epta + EPT_T10_X400 + ((vpn - 0400) >> 1);\r |
| 361 | READPT (ptewd, ptead); /* get PTE pair */\r |
| 362 | pte = (int32) ((ptewd >> ((vpn & 1)? 0: 18)) & RMASK);\r |
| 363 | pager_word = PF_VIRT | ea | ((tbl == uptbl)? PF_USER: 0) |\r |
| 364 | ((mode & PTF_WR)? PF_WRITE: 0) |\r |
| 365 | ((pte & PTE_T10_A)? PF_T10_A |\r |
| 366 | ((pte & PTE_T10_S)? PF_T10_S: 0): 0);\r |
| 367 | if (mode & PTF_MAP) pager_word = pager_word | /* map? add to pf wd */\r |
| 368 | ((pte & PTE_T10_W)? PF_T10_W: 0) | /* W, S, C bits */\r |
| 369 | ((pte & PTE_T10_S)? PF_T10_S: 0) |\r |
| 370 | ((pte & PTE_T10_C)? PF_C: 0);\r |
| 371 | if ((pte & PTE_T10_A) && (!(mode & PTF_WR) || (pte & PTE_T10_W))) {\r |
| 372 | xpte = ((pte & PTE_PPMASK) << PAG_V_PN) | /* calc exp pte */\r |
| 373 | PTBL_V | ((pte & PTE_T10_W)? PTBL_M: 0);\r |
| 374 | if (!(mode & PTF_CON)) tbl[vpn] = xpte; /* set tbl if ~cons */\r |
| 375 | return xpte;\r |
| 376 | }\r |
| 377 | PAGE_FAIL_TRAP;\r |
| 378 | } /* end TOPS10 paging */\r |
| 379 | \r |
| 380 | /* TOPS-20 paging - checked against KS10 microcode\r |
| 381 | \r |
| 382 | TOPS-20 paging has three phases:\r |
| 383 | \r |
| 384 | 1. Starting at EPT/UPT + 540 + section number, chase section pointers to\r |
| 385 | get the pointer to the section page table. In the KS10, because there\r |
| 386 | is only one section, the microcode caches the result of this evaluation.\r |
| 387 | Also, the evaluation of indirect pointers is simplified, as the section\r |
| 388 | table index is ignored.\r |
| 389 | \r |
| 390 | 2. Starting with the page map pointer, chase page pointers to get the\r |
| 391 | pointer to the page. The KS10 allows the operating system to inhibit\r |
| 392 | updating of the CST (base address = 0).\r |
| 393 | \r |
| 394 | 3. Use the page pointer to get the CST entry. If a write reference to\r |
| 395 | a writeable page, set CST_M. If CST_M is set, set M in page table.\r |
| 396 | */\r |
| 397 | \r |
| 398 | else { /* TOPS-20 paging */\r |
| 399 | int32 pmi, vpn, xpte;\r |
| 400 | int32 flg, t;\r |
| 401 | t_bool stop;\r |
| 402 | a10 pa, csta;\r |
| 403 | d10 ptr, cste;\r |
| 404 | d10 acc = PTE_T20_W | PTE_T20_C; /* init access bits */\r |
| 405 | \r |
| 406 | pager_word = PF_VIRT | ea | ((tbl == uptbl)? PF_USER: 0) |\r |
| 407 | ((mode & PTF_WR)? PF_WRITE: 0); /* set page fail word */\r |
| 408 | \r |
| 409 | /* First phase - evaluate section pointers - returns a ptr to a page map\r |
| 410 | As a single section machine, the KS10 short circuits this part of the\r |
| 411 | process. In particular, the indirect pointer calculation assumes that\r |
| 412 | the section table index will be 0. It adds the full pointer (not just\r |
| 413 | the right half) to the SPT base. If the section index is > 0, the\r |
| 414 | result is a physical memory address > 256KW. Depending on the size of\r |
| 415 | memory, the SPT fetch may or may not generate a NXM page fail. The\r |
| 416 | KS10 then ignores the section table index in fetching the next pointer.\r |
| 417 | \r |
| 418 | The KS10 KL10 memory management diagnostic (dskec.sav) tests for this\r |
| 419 | behavior with a section index of 3. However, this would be a legal\r |
| 420 | physical address in a system with 1MW. Accordingly, the simulator\r |
| 421 | special cases non-zero section indices (which can't work in any case)\r |
| 422 | to generate the right behavior for the diagnostic.\r |
| 423 | */\r |
| 424 | \r |
| 425 | vpn = PAG_GETVPN (ea); /* get virt page num */\r |
| 426 | pa = (tbl == uptbl)? upta + UPT_T20_SCTN: epta + EPT_T20_SCTN;\r |
| 427 | READPT (ptr, pa & PAMASK); /* get section 0 ptr */\r |
| 428 | for (stop = FALSE, flg = 0; !stop; flg++) { /* eval section ptrs */\r |
| 429 | acc = acc & ptr; /* cascade acc bits */\r |
| 430 | switch (T20_GETTYP (ptr)) { /* case on ptr type */\r |
| 431 | \r |
| 432 | case T20_NOA: /* no access */\r |
| 433 | default: /* undefined type */\r |
| 434 | PAGE_FAIL_TRAP; /* page fail */\r |
| 435 | \r |
| 436 | case T20_IMM: /* immediate */\r |
| 437 | stop = TRUE; /* exit */\r |
| 438 | break;\r |
| 439 | \r |
| 440 | case T20_SHR: /* shared */\r |
| 441 | pa = (int32) (spt + (ptr & RMASK)); /* get SPT idx */\r |
| 442 | READPT (ptr, pa & PAMASK); /* get SPT entry */\r |
| 443 | stop = TRUE; /* exit */\r |
| 444 | break;\r |
| 445 | \r |
| 446 | case T20_IND: /* indirect */\r |
| 447 | if (flg && (t = test_int ())) ABORT (t);\r |
| 448 | pmi = T20_GETPMI (ptr); /* get sect tbl idx */\r |
| 449 | pa = (int32) (spt + (ptr & RMASK)); /* get SPT idx */\r |
| 450 | if (pmi) { /* for dskec */\r |
| 451 | pag_nxm ((pmi << 18) | pa, REF_P, PF_OK);\r |
| 452 | PAGE_FAIL_TRAP;\r |
| 453 | }\r |
| 454 | READPT (ptr, pa & PAMASK); /* get SPT entry */\r |
| 455 | if (ptr & PTE_T20_STM) { PAGE_FAIL_TRAP; }\r |
| 456 | pa = PAG_PTEPA (ptr, pmi); /* index off page */\r |
| 457 | READPT (ptr, pa & PAMASK); /* get pointer */\r |
| 458 | break; /* continue in loop */\r |
| 459 | } /* end case */\r |
| 460 | } /* end for */\r |
| 461 | \r |
| 462 | /* Second phase - found page map ptr, evaluate page pointers */\r |
| 463 | \r |
| 464 | pa = PAG_PTEPA (ptr, vpn); /* get ptbl address */\r |
| 465 | for (stop = FALSE, flg = 0; !stop; flg++) { /* eval page ptrs */\r |
| 466 | if (ptr & PTE_T20_STM) { PAGE_FAIL_TRAP; } /* non-res? */\r |
| 467 | if (cst) { /* cst really there? */\r |
| 468 | csta = (int32) ((cst + (ptr & PTE_PPMASK)) & PAMASK);\r |
| 469 | READPT (cste, csta); /* get CST entry */\r |
| 470 | if ((cste & CST_AGE) == 0) { PAGE_FAIL_TRAP; }\r |
| 471 | cste = (cste & cstm) | pur; /* update entry */\r |
| 472 | WriteP (csta, cste); /* rewrite */\r |
| 473 | }\r |
| 474 | READPT (ptr, pa & PAMASK); /* get pointer */\r |
| 475 | acc = acc & ptr; /* cascade acc bits */\r |
| 476 | switch (T20_GETTYP (ptr)) { /* case on ptr type */\r |
| 477 | \r |
| 478 | case T20_NOA: /* no access */\r |
| 479 | default: /* undefined type */\r |
| 480 | PAGE_FAIL_TRAP; /* page fail */\r |
| 481 | \r |
| 482 | case T20_IMM: /* immediate */\r |
| 483 | stop = TRUE; /* exit */\r |
| 484 | break;\r |
| 485 | \r |
| 486 | case T20_SHR: /* shared */\r |
| 487 | pa = (int32) (spt + (ptr & RMASK)); /* get SPT idx */\r |
| 488 | READPT (ptr, pa & PAMASK); /* get SPT entry */\r |
| 489 | stop = TRUE; /* exit */\r |
| 490 | break;\r |
| 491 | \r |
| 492 | case T20_IND: /* indirect */\r |
| 493 | if (flg && (t = test_int ())) ABORT (t);\r |
| 494 | pmi = T20_GETPMI (ptr); /* get section index */\r |
| 495 | pa = (int32) (spt + (ptr & RMASK)); /* get SPT idx */\r |
| 496 | READPT (ptr, pa & PAMASK); /* get SPT entry */\r |
| 497 | pa = PAG_PTEPA (ptr, pmi); /* index off page */\r |
| 498 | break; /* continue in loop */\r |
| 499 | } /* end case */\r |
| 500 | } /* end for */\r |
| 501 | \r |
| 502 | /* Last phase - have final page pointer, check modifiability */\r |
| 503 | \r |
| 504 | if (ptr & PTE_T20_STM) { PAGE_FAIL_TRAP; } /* non-resident? */\r |
| 505 | if (cst) { /* CST really there? */\r |
| 506 | csta = (int32) ((cst + (ptr & PTE_PPMASK)) & PAMASK);\r |
| 507 | READPT (cste, csta); /* get CST entry */\r |
| 508 | if ((cste & CST_AGE) == 0) { PAGE_FAIL_TRAP; }\r |
| 509 | cste = (cste & cstm) | pur; /* update entry */\r |
| 510 | }\r |
| 511 | else cste = 0; /* no, entry = 0 */\r |
| 512 | pager_word = pager_word | PF_T20_DN; /* set eval done */\r |
| 513 | xpte = ((int32) ((ptr & PTE_PPMASK) << PAG_V_PN)) | PTBL_V;\r |
| 514 | if (mode & PTF_WR) { /* write? */\r |
| 515 | if (acc & PTE_T20_W) { /* writable? */\r |
| 516 | xpte = xpte | PTBL_M; /* set PTE M */\r |
| 517 | cste = cste | CST_M; /* set CST M */\r |
| 518 | }\r |
| 519 | else { PAGE_FAIL_TRAP; } /* no, trap */\r |
| 520 | }\r |
| 521 | if (cst) WriteP (csta, cste); /* write CST entry */\r |
| 522 | if (mode & PTF_MAP) pager_word = pager_word | /* map? more in pf wd */\r |
| 523 | ((xpte & PTBL_M)? PF_T20_M: 0) | /* M, W, C bits */\r |
| 524 | ((acc & PTE_T20_W)? PF_T20_W: 0) |\r |
| 525 | ((acc & PTE_T20_C)? PF_C: 0);\r |
| 526 | if (!(mode & PTF_CON)) tbl[vpn] = xpte; /* set tbl if ~cons */\r |
| 527 | return xpte;\r |
| 528 | } /* end TOPS20 paging */\r |
| 529 | }\r |
| 530 | \r |
| 531 | /* Set up pointers for AC, memory, and process table access */\r |
| 532 | \r |
| 533 | void set_dyn_ptrs (void)\r |
| 534 | {\r |
| 535 | int32 t;\r |
| 536 | \r |
| 537 | if (PAGING) {\r |
| 538 | ac_cur = &acs[UBR_GETCURAC (ubr) * AC_NUM];\r |
| 539 | ac_prv = &acs[UBR_GETPRVAC (ubr) * AC_NUM];\r |
| 540 | if (TSTF (F_USR)) ptbl_cur = ptbl_prv = &uptbl[0];\r |
| 541 | else {\r |
| 542 | ptbl_cur = &eptbl[0];\r |
| 543 | ptbl_prv = TSTF (F_UIO)? &uptbl[0]: &eptbl[0];\r |
| 544 | }\r |
| 545 | }\r |
| 546 | else {\r |
| 547 | ac_cur = ac_prv = &acs[0];\r |
| 548 | ptbl_cur = ptbl_prv = &physptbl[0];\r |
| 549 | }\r |
| 550 | t = EBR_GETEBR (ebr);\r |
| 551 | epta = t << PAG_V_PN;\r |
| 552 | if (Q_ITS) upta = (int32) ubr & PAMASK;\r |
| 553 | else {\r |
| 554 | t = UBR_GETUBR (ubr);\r |
| 555 | upta = t << PAG_V_PN;\r |
| 556 | }\r |
| 557 | return;\r |
| 558 | }\r |
| 559 | \r |
| 560 | /* MAP instruction, TOPS-10 and TOPS-20 only\r |
| 561 | \r |
| 562 | According to the KS-10 ucode, map with paging disabled sets\r |
| 563 | "accessible, writeable, software", regardless of whether\r |
| 564 | TOPS-10 or TOPS-20 paging is implemented\r |
| 565 | */\r |
| 566 | \r |
| 567 | d10 map (a10 ea, int32 prv)\r |
| 568 | {\r |
| 569 | int32 xpte;\r |
| 570 | d10 val = (TSTF (F_USR)? PF_USER: 0);\r |
| 571 | \r |
| 572 | if (!PAGING) return (val | PF_T10_A | PF_T10_W | PF_T10_S | ea);\r |
| 573 | xpte = ptbl_fill (ea, prv? ptbl_prv: ptbl_cur, PTF_MAP); /* get exp pte */\r |
| 574 | if (xpte) val = (pager_word & ~PAMASK) | PAG_XPTEPA (xpte, ea);\r |
| 575 | else {\r |
| 576 | if (pager_word & PF_HARD) val = pager_word; /* hard error */\r |
| 577 | else val = val | PF_VIRT | ea; /* inaccessible */\r |
| 578 | }\r |
| 579 | return val;\r |
| 580 | }\r |
| 581 | \r |
| 582 | /* Mapping routine for console */\r |
| 583 | \r |
| 584 | a10 conmap (a10 ea, int32 mode, int32 sw)\r |
| 585 | {\r |
| 586 | int32 xpte, *tbl;\r |
| 587 | \r |
| 588 | if (!PAGING) return ea;\r |
| 589 | set_dyn_ptrs (); /* in case changed */\r |
| 590 | if (sw & SWMASK ('E')) tbl = eptbl;\r |
| 591 | else if (sw & SWMASK ('U')) tbl = uptbl;\r |
| 592 | else tbl = ptbl_cur;\r |
| 593 | xpte = ptbl_fill (ea, tbl, mode);\r |
| 594 | if (xpte) return PAG_XPTEPA (xpte, ea);\r |
| 595 | else return MAXMEMSIZE;\r |
| 596 | }\r |
| 597 | \r |
| 598 | /* Common pager instructions */\r |
| 599 | \r |
| 600 | t_bool clrpt (a10 ea, int32 prv)\r |
| 601 | {\r |
| 602 | int32 vpn = PAG_GETVPN (ea); /* get page num */\r |
| 603 | \r |
| 604 | if (Q_ITS) { /* ITS? */\r |
| 605 | uptbl[vpn & ~1] = 0; /* clear double size */\r |
| 606 | uptbl[vpn | 1] = 0; /* entries in */\r |
| 607 | eptbl[vpn & ~1] = 0; /* both page tables */\r |
| 608 | eptbl[vpn | 1] = 0;\r |
| 609 | }\r |
| 610 | else {\r |
| 611 | uptbl[vpn] = 0; /* clear entries in */\r |
| 612 | eptbl[vpn] = 0; /* both page tables */\r |
| 613 | }\r |
| 614 | return FALSE;\r |
| 615 | } \r |
| 616 | \r |
| 617 | t_bool wrebr (a10 ea, int32 prv)\r |
| 618 | {\r |
| 619 | ebr = ea & EBR_MASK; /* store EBR */\r |
| 620 | pag_reset (&pag_dev); /* clear page tables */\r |
| 621 | set_dyn_ptrs (); /* set dynamic ptrs */\r |
| 622 | return FALSE;\r |
| 623 | }\r |
| 624 | \r |
| 625 | t_bool rdebr (a10 ea, int32 prv)\r |
| 626 | {\r |
| 627 | Write (ea, (ebr & EBR_MASK), prv);\r |
| 628 | return FALSE;\r |
| 629 | }\r |
| 630 | \r |
| 631 | t_bool wrubr (a10 ea, int32 prv)\r |
| 632 | {\r |
| 633 | d10 val = Read (ea, prv);\r |
| 634 | d10 ubr_mask = (Q_ITS)? PAMASK: UBR_UBRMASK; /* ITS: ubr is wd addr */\r |
| 635 | \r |
| 636 | if (val & UBR_SETACB) ubr = ubr & ~UBR_ACBMASK; /* set AC's? */\r |
| 637 | else val = val & ~UBR_ACBMASK; /* no, keep old val */\r |
| 638 | if (val & UBR_SETUBR) { /* set UBR? */\r |
| 639 | ubr = ubr & ~ubr_mask;\r |
| 640 | pag_reset (&pag_dev); /* yes, clr pg tbls */\r |
| 641 | }\r |
| 642 | else val = val & ~ubr_mask; /* no, keep old val */\r |
| 643 | ubr = (ubr | val) & (UBR_ACBMASK | ubr_mask);\r |
| 644 | set_dyn_ptrs ();\r |
| 645 | return FALSE;\r |
| 646 | }\r |
| 647 | \r |
| 648 | t_bool rdubr (a10 ea, int32 prv)\r |
| 649 | {\r |
| 650 | ubr = ubr & (UBR_ACBMASK | (Q_ITS? PAMASK: UBR_UBRMASK));\r |
| 651 | Write (ea, UBRWORD, prv);\r |
| 652 | return FALSE;\r |
| 653 | }\r |
| 654 | \r |
| 655 | t_bool wrhsb (a10 ea, int32 prv)\r |
| 656 | {\r |
| 657 | hsb = Read (ea, prv) & PAMASK;\r |
| 658 | return FALSE;\r |
| 659 | }\r |
| 660 | \r |
| 661 | t_bool rdhsb (a10 ea, int32 prv)\r |
| 662 | {\r |
| 663 | Write (ea, hsb, prv);\r |
| 664 | return FALSE;\r |
| 665 | }\r |
| 666 | \r |
| 667 | /* TOPS20 pager instructions */\r |
| 668 | \r |
| 669 | t_bool wrspb (a10 ea, int32 prv)\r |
| 670 | {\r |
| 671 | spt = Read (ea, prv);\r |
| 672 | return FALSE;\r |
| 673 | }\r |
| 674 | \r |
| 675 | t_bool rdspb (a10 ea, int32 prv)\r |
| 676 | {\r |
| 677 | Write (ea, spt, prv);\r |
| 678 | return FALSE;\r |
| 679 | }\r |
| 680 | \r |
| 681 | t_bool wrcsb (a10 ea, int32 prv)\r |
| 682 | {\r |
| 683 | cst = Read (ea, prv);\r |
| 684 | return FALSE;\r |
| 685 | }\r |
| 686 | \r |
| 687 | t_bool rdcsb (a10 ea, int32 prv)\r |
| 688 | {\r |
| 689 | Write (ea, cst, prv);\r |
| 690 | return FALSE;\r |
| 691 | }\r |
| 692 | \r |
| 693 | t_bool wrpur (a10 ea, int32 prv)\r |
| 694 | {\r |
| 695 | pur = Read (ea, prv);\r |
| 696 | return FALSE;\r |
| 697 | }\r |
| 698 | \r |
| 699 | t_bool rdpur (a10 ea, int32 prv)\r |
| 700 | {\r |
| 701 | Write (ea, pur, prv);\r |
| 702 | return FALSE;\r |
| 703 | }\r |
| 704 | \r |
| 705 | t_bool wrcstm (a10 ea, int32 prv)\r |
| 706 | {\r |
| 707 | cstm = Read (ea, prv);\r |
| 708 | if ((cpu_unit.flags & UNIT_T20) && (ea == 040127))\r |
| 709 | cstm = 0770000000000;\r |
| 710 | return FALSE;\r |
| 711 | }\r |
| 712 | \r |
| 713 | t_bool rdcstm (a10 ea, int32 prv)\r |
| 714 | {\r |
| 715 | Write (ea, cstm, prv);\r |
| 716 | return FALSE;\r |
| 717 | }\r |
| 718 | \r |
| 719 | /* ITS pager instructions\r |
| 720 | The KS10 does not implement the JPC option.\r |
| 721 | */\r |
| 722 | \r |
| 723 | t_bool clrcsh (a10 ea, int32 prv)\r |
| 724 | {\r |
| 725 | return FALSE;\r |
| 726 | }\r |
| 727 | \r |
| 728 | t_bool ldbr1 (a10 ea, int32 prv)\r |
| 729 | {\r |
| 730 | dbr1 = ea;\r |
| 731 | pag_reset (&pag_dev);\r |
| 732 | return FALSE;\r |
| 733 | }\r |
| 734 | \r |
| 735 | t_bool sdbr1 (a10 ea, int32 prv)\r |
| 736 | {\r |
| 737 | Write (ea, dbr1, prv);\r |
| 738 | return FALSE;\r |
| 739 | }\r |
| 740 | \r |
| 741 | t_bool ldbr2 (a10 ea, int32 prv)\r |
| 742 | {\r |
| 743 | dbr2 = ea;\r |
| 744 | pag_reset (&pag_dev);\r |
| 745 | return FALSE;\r |
| 746 | }\r |
| 747 | \r |
| 748 | t_bool sdbr2 (a10 ea, int32 prv)\r |
| 749 | {\r |
| 750 | Write (ea, dbr2, prv);\r |
| 751 | return FALSE;\r |
| 752 | }\r |
| 753 | \r |
| 754 | t_bool ldbr3 (a10 ea, int32 prv)\r |
| 755 | {\r |
| 756 | dbr3 = ea;\r |
| 757 | pag_reset (&pag_dev);\r |
| 758 | return FALSE;\r |
| 759 | }\r |
| 760 | \r |
| 761 | t_bool sdbr3 (a10 ea, int32 prv)\r |
| 762 | {\r |
| 763 | Write (ea, dbr3, prv);\r |
| 764 | return FALSE;\r |
| 765 | }\r |
| 766 | \r |
| 767 | t_bool ldbr4 (a10 ea, int32 prv)\r |
| 768 | {\r |
| 769 | dbr4 = ea;\r |
| 770 | pag_reset (&pag_dev);\r |
| 771 | return FALSE;\r |
| 772 | }\r |
| 773 | \r |
| 774 | t_bool sdbr4 (a10 ea, int32 prv)\r |
| 775 | {\r |
| 776 | Write (ea, dbr4, prv);\r |
| 777 | return FALSE;\r |
| 778 | }\r |
| 779 | \r |
| 780 | t_bool wrpcst (a10 ea, int32 prv)\r |
| 781 | {\r |
| 782 | pcst = Read (ea, prv);\r |
| 783 | return FALSE;\r |
| 784 | }\r |
| 785 | \r |
| 786 | t_bool rdpcst (a10 ea, int32 prv)\r |
| 787 | {\r |
| 788 | Write (ea, pcst, prv);\r |
| 789 | return FALSE;\r |
| 790 | }\r |
| 791 | \r |
| 792 | t_bool lpmr (a10 ea, int32 prv)\r |
| 793 | {\r |
| 794 | d10 val;\r |
| 795 | \r |
| 796 | val = Read (ADDA (ea, 2), prv);\r |
| 797 | dbr1 = (a10) (Read (ea, prv) & AMASK);\r |
| 798 | dbr2 = (a10) (Read (ADDA (ea, 1), prv) & AMASK);\r |
| 799 | quant = val;\r |
| 800 | pag_reset (&pag_dev);\r |
| 801 | return FALSE;\r |
| 802 | }\r |
| 803 | \r |
| 804 | t_bool spm (a10 ea, int32 prv)\r |
| 805 | {\r |
| 806 | \r |
| 807 | ReadM (ADDA (ea, 2), prv);\r |
| 808 | Write (ea, dbr1, prv);\r |
| 809 | Write (ADDA (ea, 1), dbr2, prv);\r |
| 810 | Write (ADDA (ea, 2), quant, prv);\r |
| 811 | return FALSE;\r |
| 812 | }\r |
| 813 | \r |
| 814 | /* Simulator interface routines */\r |
| 815 | \r |
| 816 | t_stat pag_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw)\r |
| 817 | {\r |
| 818 | int32 tbln = uptr - pag_unit;\r |
| 819 | \r |
| 820 | if (addr >= PTBL_MEMSIZE) return SCPE_NXM;\r |
| 821 | *vptr = tbln? uptbl[addr]: eptbl[addr];;\r |
| 822 | return SCPE_OK;\r |
| 823 | }\r |
| 824 | \r |
| 825 | t_stat pag_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw)\r |
| 826 | {\r |
| 827 | int32 tbln = uptr - pag_unit;\r |
| 828 | \r |
| 829 | if (addr >= PTBL_MEMSIZE) return SCPE_NXM;\r |
| 830 | if (tbln) uptbl[addr] = (int32) val & PTBL_MASK;\r |
| 831 | else eptbl[addr] = (int32) val & PTBL_MASK;\r |
| 832 | return SCPE_OK;\r |
| 833 | }\r |
| 834 | \r |
| 835 | t_stat pag_reset (DEVICE *dptr)\r |
| 836 | {\r |
| 837 | int32 i;\r |
| 838 | \r |
| 839 | for (i = 0; i < PTBL_MEMSIZE; i++) {\r |
| 840 | eptbl[i] = uptbl[i] = 0;\r |
| 841 | physptbl[i] = (i << PAG_V_PN) + PTBL_M + PTBL_V;\r |
| 842 | }\r |
| 843 | return SCPE_OK;\r |
| 844 | }\r |