VAX 11/.750 Boot ROM code makes non-longword memory references to MassBus and Unibus register space. Minor changes were necessary to allow this behavior which was architecturally undefined behavior, but had real code which depended on it.
Added a BOOTDEV option to the CPU to reflect the 4 position boot device selection switch on real VAX 11/750 hardware.
The UBA750 initial state started with the UBA map registers validly mapping the first 256KB of RAM to Unibus space.
Added simulated PCS/WCS memory which boot code on some operating systems (Ultrix and other BSD derived systems) automatically loaded on the VAX 11/750. PCS/WCS was also automatically loaded by the newer versions of the BOOT ROMs.
The reboot command code was not defined, and even when defined, it didn't get executed before the simulated code executed a HALT instruction. Solved by scheduling immediate execution of reboot command.
VIDEO:
- Make mouse motion activity consistent with SDL relative direction. Add error output when mouse events are discarded due to queue full.
If a client application delivers motion information in a different relative sense, then that application needs to make the adjustments from the SDL standard direction.
- Added SHOW dev VIDEO capability to describe the underlying SDL video capabilities of the current SDL library and host execution environment.
- Force software based rendering under SDL2. Enhanced debug info.
- Added host OS cursor integration support.
- Reorganize libSDL vs libSDL2 version implementation to leverage common logic without replication.
QVSS:
- Coalesced adjacent screen row updates to minimize vid_draw operations
- Report all relative mouse motion in the mouse position register AND mouse motion data.
- Added debugging information for cursor and scan line map updates
- Add option "SET QVSS CAPTURED" to force capture input mode.
The native VMB.EXE program historically supported a Boot Block oriented
boot if Bit 3 of the parameter register (R5) was set when VMB was invoked.
This Boot Block boot operation reads sector 0 into memory and starts
execution at offset 2 of the data block in memory.
When portitions of VMB were migrated into ROM to support the earliest
MicroVAX system (MicroVAX I) and all subsequent ROM based VMB versions
the concept of Boot Block booting was extended in these ROM VMB
implementations. The change in boot block booting functionality included
several features:
1) If a normal boot attempt to a device failed (due to VMB not being
able to locate a secondary bootstrap program), a boot block boot is
automatically attempted. If the Bit 3 of R5 was set, then the
initial search for a secondary boot block was avoided and a boot
block boot was immediately attempted.
2) When performing a boot block boot, the sector 0 contents are examined
and if these contents conform to the pattern defined for ROM based
(PRA0) booting, the ROM format Offset, Size, and Starting address
information is used directly by VMB to load a program into memory
and control is transferred to that program. If the contents of
sector 0 do not fit the pattern required for ROM based booting, then
the code in sector 0 is executed directly starting at offset 2,
the same as was originally done with the non ROM based VMB versions.
Note that this extended behavior allows sector 0 to contain very little
information and quite possibly no actual boot code.
Developers of alternate operating systems for VAX computers noticed the ROM
based boot block behavior and changed their installation media AND the disk
structures to only provide the minimal boot information required on the
systems with VMB installed in ROM.
Since, when this active development of these alternate operating systems for
VAX computers was happening, the vast majority of development and new system
deployments were to hardware which had ROM base VMB, no one noticed that
older systems which booted with the non ROM based VMB could no longer boot
from new install media or disks formatted with these operating systems.
This patch addeds the ROM based VMB boot block boot functionality to the
original dynamically loaded VMB.EXE used by the older systems to boot.
The patch overwrites some VMB code which exists to support NVAX(1302) and
Neon-V(1701) systems. If simh simulators for these systems are ever built
an alternate location must be found to accomodate this extended logic
Removed the recently added SET CPU IDLE=SYSV since any SET CPU IDLE command will work for SysV. Existing configurations probably did a SET CPU IDLE=32V which works fine now.
Changed things based on the realization that ANY branch instruction which tests memory and then branches to itself is an idle loop if the branch is taken. This is without regard to address space, access mode, If interrupts are disabled, then it is a hung system and the simulation should halt.
Writing to the TXCS register always updated the transmit enable mask, even when the "Write Mask Now" bit was not set. That bit needs to be set for the write to the register to actually update the transmit enable mask.
CPU Idle detection for this OS is now supported and the combination of SET CPU IDLE=ULTRIX-1.X and explicitly using a DEQNA device (SET XQ TYPE=DEQNA) will enable the automatic enabling of device interrupt generation.
The goals here being to simplify calling code while getting consistent output delivered everywhere it may be useful.
Modified most places which explicitly used sim_log or merely called printf to now avoid doing that and merely call sim_printf().
Design Notes for Fixing VAX Unaligned Access to IO and Register Space
Problem Statement: VAX unaligned accesses are handled by reading the
surrounding longword (or longwords) and
a) for reads, extracting the addressed addressed word or longword
b) for writes, inserting the addressed word or longword and then
writing the surrounding longword (or longwords) back
This is correct for all memory cases. On the 11/780, the unaligned
access to register or IO space causes an error, as it should. On
CVAX, it causes incorrect behavior, by either performing too many
QBus references, or performing read-modify-writes instead of pure
writes, or accessing the wrong Qbus locations.
The problem cannot be trivially solved with address manipulation.
The core issues is that on CVAX, unaligned access is done to
exactly as many bytes as are required, using a base longword
address and a byte mask. There are five cases, corresponding to
word and longword lengths, and byte offsets 1, 2 (longword only),
and 3. Further, behavior is different for reads and writes, because
the Qbus always performs word operations on reads, leaving it to
the processor to extract a byte if needed.
Conceptual design: Changes in vax_mmu.c:
Unaligned access is done with two separate physical addresses, pa
and pa1, because if the access crosses a page boundary, pa1 may
not be contiguous with pa. It's worth noting that in an unaligned
access, the low part of the data begins at pa (complete with byte
offset), but the high parts begins at pa1 & ~03 (always in the
low-order end of the second longword).
To handle unaligned data, we will add two routines for read and
write unaligned:
data = ReadU (pa, len);
WriteU (pa, len, val);
Note that the length can be 1, 2, or 3 bytes. For ReadU, data is
return right-aligned and masked. For WriteU, val is expected to
be right-aligned and masked.
The read-unaligned flows are changed as follows:
if (mapen && ((off + lnt) > VA_PAGSIZE)) { /* cross page? */
vpn = VA_GETVPN (va + lnt); /* vpn 2nd page */
tbi = VA_GETTBI (vpn);
xpte = (va & VA_S0)? stlb[tbi]: ptlb[tbi]; /* access tlb */
if (((xpte.pte & acc) == 0) || (xpte.tag != vpn) ||
((acc & TLB_WACC) && ((xpte.pte & TLB_M) == 0)))
xpte = fill (va + lnt, lnt, acc, NULL); /* fill if needed */
pa1 = ((xpte.pte & TLB_PFN) | VA_GETOFF (va + 4)) & ~03;
}
else pa1 = ((pa + 4) & PAMASK) & ~03; /* not cross page */
bo = pa & 3;
if (lnt >= L_LONG) { /* lw unaligned? */
sc = bo << 3;
wl = ReadU (pa, L_LONG - bo); /* read both fragments */
wh = ReadU (pa1, bo); /* extract */
return ((wl | (wh << (32 - sc))) & LMASK);
}
else if (bo == 1) /* read within lw */
return ReadU (pa, L_WORD);
else {
wl = ReadU (pa, L_BYTE); /* word cross lw */
wh = ReadU (pa1, L_BYTE); /* read, extract */
return (wl | (wh << 8));
}
These are not very different, but they do reflect that ReadU returns
right-aligned and properly masked data, rather than the encapsulating
longword.
The write-unaligned flows change rather more drastically:
if (mapen && ((off + lnt) > VA_PAGSIZE)) {
vpn = VA_GETVPN (va + 4);
tbi = VA_GETTBI (vpn);
xpte = (va & VA_S0)? stlb[tbi]: ptlb[tbi]; /* access tlb */
if (((xpte.pte & acc) == 0) || (xpte.tag != vpn) ||
((xpte.pte & TLB_M) == 0))
xpte = fill (va + lnt, lnt, acc, NULL);
pa1 = ((xpte.pte & TLB_PFN) | VA_GETOFF (va + 4)) & ~03;
}
else pa1 = ((pa + 4) & PAMASK) & ~03;
bo = pa & 3;
if (lnt >= L_LONG) {
sc = bo << 3;
WriteU (pa, L_LONG - bo, val & insert[L_LONG - bo]);
WriteU (pa, bo, (val >> (32 - sc)) & insert[bo]);
}
else if (bo == 1) /* read within lw */
WriteU (pa, L_WORD, val & WMASK);
else { /* word cross lw */
WriteU (pa, L_BYTE, val & BMASK);
WriteU (pa, L_BYTE, (val >> 8) & BMASK);
}
return;
}
Note that all the burden here has been thrown on the WriteU routine.
-------------
ReadU is the simpler of the two routines that needs to be written.
It will handle memory reads and defer register and IO space to
model-specific unaligned handlers.
int32 ReadU (uint32 pa, int32 lnt)
{
int32 dat;
int32 sc = (pa & 3) << 3;
if (ADDR_IS_MEM (pa))
dat = M[pa >> 2];
else {
mchk = REF_V;
if (ADDR_IS_IO (pa))
dat = ReadIOU (pa, lnt);
else dat = ReadRegU (pa, lnt);
}
return ((dat >> sc) & insert[lnt]);
}
Note that the ReadIOU and ReadRegU return a "full longword," just
like their aligned counterparts, and ReadU right-aligns the result,
just as ReadB, ReadW, and ReadL do.
WriteU must handle the memory read-modify-write sequence. However,
it defers register and IO space to model-specific unaligned handlers.
void WriteU (uint32 pa, int32 lnt, int32 val)
{
if (ADDR_IS_MEM (pa)) {
int32 bo = pa & 3;
int32 sc = bo << 3;
M[pa >> 2] = (M[pa >> 2] & ~(insert[len] << sc) | (val << sc);
}
else if ADDR_IS_IO (pa)
WriteIOU (pa, lnt, val);
else WriteRegU (pa, lnt, val);
return;
}
--------------
For the 11/780, ReadIOU, ReadRegU, WriteIOU, and WriteRegU all do the
same thing: they throw an SBI machine check. We can write explicit
routines to do this (and remove the unaligned checks from all the
normal adapter flows), or leave things as they are and simply define
the four routines as macros that go to the normal routines. So there's
very little to do.
On CVAX, I suspect that ReadRegU and WriteRegU behave like the
normal routines. The CVAX specs don't say much, but CMCTL (the memory
controller) notes that it ignores the byte mask and treats every
access as an aligned longword access. I suspect this is true for
the other CVAX support chips, but I no longer have chip specs.
The Qbus, on the other hand... that's a fun one. Note that all of
these cases are presented to the existing aligned IO routine:
bo = 0, byte, word, or longword length
bo = 2, word
bo = 1, 2, 3, byte length
All the other cases are going to end up at ReadIOU and WriteIOU,
and they must turn the request into the exactly correct number of
Qbus accesses AND NO MORE, because Qbus reads can have side-effects,
and word read-modify-write is NOT the same as a byte write.
The read cases are:
bo = 0, byte or word - read one word
bo = 1, byte - read one word
bo = 2, byte or word - read one word
bo = 3, byte - read one word
bo = 0, triword - read two words
bo = 1, word or triword - read two words
ReadIOU is very similar to the existing ReadIO:
int32 ReadIOU (uint32 pa, int32 lnt)
{
int32 iod;
iod = ReadQb (pa); /* wd from Qbus */
if ((lnt + (pa & 1)) <= 2) /* byte or word & even */
iod = iod << ((pa & 2)? 16: 0); /* one op */
else iod = (ReadQb (pa + 2) << 16) | iod; /* two ops, get 2nd wd */
SET_IRQL;
return iod;
}
The write cases are:
bo = x, lnt = byte - write one byte
bo = 0 or 2, lnt = word - write one word
bo = 1, lnt = word - write two bytes
bo = 0, lnt = triword - write word, byte
bo = 1, lnt = triword - write byte, word
WriteIOU is similar to the existing WriteIO:
void WriteIO (uint32 pa, int32 val, int32 lnt)
{
switch (lnt) {
case L_BYTE: /* byte */
WriteQb (pa, val & BMASK, WRITEB);
break;
case L_WORD: /* word */
if (pa & 1) { /* odd addr? */
WriteQb (pa, val & BMASK, WRITEB);
WriteQb (pa + 1, (val >> 8) & BMASK, WRITEB);
}
else WriteQb (pa, val, WRITE);
break;
case 3: /* triword */
if (pa & 1) { /* odd addr? */
WriteQb (pa, val & BMASK, WRITEB);
WriteQb (pa + 1, (val >> 8) & WMASK, WRITE);
}
else {
WriteQb (pa, val & WMASK, WRITE);
WriteQb (pa + 2, (val >> 16) & BMASK, WRITEB);
}
break;
}
SET_IRQL;
return;
}
-----------------
I think this handles all the cases.
/Bob Supnik
Conflicts:
VAX/vax780_defs.h
VAX/vax_mmu.c
VAX/vaxmod_defs.h
This is the results of external KDP development activities. The KDP side done by Timothe Litt and DMC and DUP by Mark Pizzolato
Additionally, other PDP10 updates from Timothe Litt