HARDWARE INFORMATION There are nine special registers, as follows
SPECIAL REGISTERS
FLAGS
PDBR
INTVEC
CGBR
CGLEN
DEBUG
TIMER
SYSSP
SYSFP
A single word containing all of the one-bit flags Page Directory Base Register
The address of the interrupt vector
Call Gate Base Register
Number of call gates
If the value of PC ever = this value, a debug interrupt is signalled Reduced by 1 after each instruction, causes timer interrupt when zero System stack pointer. If in system mode, equivalent to SP
System frame pointer, not so useful.
The assembler understands the names of these registers (put a $ sign in front), they stand for the numbers 0 to 9 in instruction operands.
There are two instructions that directly access the special registers: GETSR loads a special register value into a normal register SETSR stores a normal register value into a special register
Example: how to set the TIMER register to 100: LOAD R1, 100
SETSR R1, $TIMER
The value stored in $PDBR is always treated as a physical memory address.
The values stored in $INTVEC, $CGBR, $DEBUG, $SYSSP, and $SYSFP are treated as virtual addresses when virtual memory is turned on.
FLAGS
There are seven one-bit CPU flags, as follows
R Indicates that the CPU is running, not halted
Z Zero. Set by some instructions to indicate a zero (or equal) result.
N Negative. Set by some instructions to indicate a negative result.
ERR Error. Used only by the PERI instruction. Zero means success.
SYS Set when CPU is in system mode, Zero when in user mode.
IP Interrupt in progress. Set to 1 to ignore interrupts.
VM Virtual Memory. If zero, all memory accesses use physical addresses,
if set, page tables must be correctly set up, all memory addresses are translated. The final three, SYS, IP, and VM, may only be modified when the CPU is in system mode.
At start-up, SYS=1, IP=1, VM=0.
The assembler understands the names of these flags (put a $ sign in front), they stand for the numbers 0 to 6 in instruction operands.
There are two instructions that directly access the special registers:
GETFL loads the value of a single flag into a register
SETFL sets a single flag equal to a register value (0 for off, non-0 for on)
The COMP and COMPZ instructions set or clear both Z and N, depending on the result. The JCOND instruction jumps if the flags have a particular combination of values.
All the flag values may be read at once, using the GETSR instruction on the $FLAGS special register. The flags occupy the least significant bits of the value, in the order shown above. R is the least significant bit, VM is bit 6 (equivalent value 64).
All the flag values may be set at once using the SETSR instruction on the $FLAGS special register.
Example: Turn the SYS flag off, and the VM flag on, leaving other flags untouched: GETSR R1, $FLAGS
CBIT R1, $SYS SBIT R1, $VM SETSR R1, $FLAGS
The special instruction FLAGSJ sets all the flags at once, and causes an unconditional jump by setting the PC. The only real point of this weird instruction is that it lets you turn on virtual memory without crashing the system. As soon as the VM flag is turned on, virtual-to-physical address translation begins for all memory accesses, so in the example above, if the program counter = 101 for the first instruction the GETSR is fetched from physical location 101, the CBIT is fetched from physical location 102, the SBIT is fetched from physical location 103, then suddenly physical addresses are not used any more, and the next instruction is fetched from virtual address 104. Unless virtual address 104 maps to physical address 104 (which would not make much sense), everything fails. This sequence:
GETSR R1, $FLAGS CBIT R1, $SYS SBIT R1, $VM FLAGSJ R1, xxx
is safe. Of course xxx should be replaced by the correct virtual address for program continuation.
BIT RANGES
The instructions EXBR, DPBR, etc extract or deposit a sequence of consecutive bits from within a single word. The desired bits are described by a single 32 bit value constructed thus:
5 least significant bits: number of bits in the range, with 00000 indicating 32.
5 next bits: the number of bits to the right of the range within its word.
22 most significant bits: the number of whole words to be skipped before extracting the bits.
EXBR and DPBR work directly on their operand, so the 22 most significant bits are ignored. EXBRV and DPBRV treat their operand as the address of the first word in a segment of memory.
INTERRUPTS
There are interrupts that represent a fatal problem (such as a user mode program attempting a privileged operation) and there are interrupts that represent some useful notification (such as keyboard input ready, or countdown timer reached zero). If interrupts are being processed (that is, the IP flag is 0, and the INTVEC special register contains the address of a proper interrupt vector), then all interrupts are trappable, regardless of how fatal they are.
If interrupts are being ignored (IP flag is 1), then fatal interrupts still stop a program, but notification interrupts are just ignored.
If interrupts are being accepted (IP=0) and a particular interrupt arises, but the interrupt vector is invalid, a second interrupt, INTRFAULT, is signalled. This may also be trapped, but given that it is caused by the failure to correctly process another interrupt, it will probably turn out to be fatal.
Beware of this. Problems with regular programs (system or user mode) cause interrupts, and that is fine. The interrupt gives the system a chance to correct whatever condition caused it. BUT interrupt handling functions have no backup. If an interrupt handler causes a non-trivial interrupt, even a page fault, it will normally be fatal.
The INTRFAULT interrupt is the last chance to avoid a big crash. If you have a handling function for INTRFAULT stored in the interrupt vector, it will be called if a fatal interrupt occurs during interrupt processing, but it will not be able to return to processing the original interrupt after fixing the situation.
There are 14 interrupts defined, each with a name known to the assembler. Their names all begin with IV$. An interrupt vector is really an array, and must be at least 14 words long. To be used, its address must be stored in the special register INTVEC. Each entry in the array is either zero (the corresponding interrupt will not be handled) or the address of an almost perfectly normal function that will be called automatically whenever the relevant interrupt occurs. The only special requirement is that interrupt handling functions must use IRET in all places instead of RET.
The defined interrupts are:
IV$NONE
IV$MEMORY
IV$PAGEFAULT
IV$UNIMPOP
IV$HALT
IV$DIVZERO
IV$UNWROP
IV$TIMER
IV$PRIVOP
IV$KEYBD
IV$BADCALL
IV$PAGEPRIV
IV$DEBUG
IV$INTRFAULT
=0: =1: =2: =3: =4: =5: =6: =7: =8: =9: = 10: = 11: = 12: = 13:
(not a real interrupt code)
Physical memory access failed
Page fault
Unimplemented operation code (i.e. instruction opcode wrong) HALT instruction executed
Division by zero
Unwritable instruction operand (e.g. INC 72)
Countdown timer reached zero
Privileged operation attempted by user mode program
at least one keyboard character typed and ready
Bad SYSCALL index (i.e. <0 or >=$CGLEN)
User mode access to system mode page
PC=$DEBUG trap
Failure to process interrupt.
The IV$ values are the positions in the interrupt vector where the handler functions address should be stored.
Example: How to set up an interrupt handler that automatically prints a dot whenever a keyboard key is pressed, and a star whenever another 5000 instructions have been executed
LOAD R1, STORE R1, LOAD R1, STORE R1, LOAD R1, SETSR R1, LOAD R1, SETFL R1, LOAD R1, SETSR R1,
TIMHANDLER:
LOAD R1,
CALL PRINTCHARACTER LOAD R1, 5000 SETSR R1, $TIMER IRET
KBHANDLER:
LOAD R1, .
CALL PRINTCHARACTER
TIMHANDLER
[IVEC+IV$TIMER]
KBHANDLER
[IVEC+IV$KEYBD]
IVEC
$INTVEC
0
$IP
5000
$TIMER
*
NOTE this interrupt will be repeatedly signalled until the character is consumed.
IRET
IVEC:
.SPACE 16
ACTIONS AUTOMATICALLY PERFORMED WHEN AN INTERRUPT OCCURS, IF IP FLAG IS 0.
oldflags =FLAGSregister
flagSYSturnedon. (i.e.nowusingsystemSPandsystemstack) flag IP turned on.
PUSH R0
PUSH R1
PUSH R11
PUSH R12
PUSH SP
// which you would have to write somewhere
PUSH FP
PUSH PC
PUSH additional interrupt information if available PUSH interrupt-causing address
PUSH interrupt code (i.e. position in interrupt vector) PUSH oldflags
PUSH 40
PC = memory[$INTVEC + interrupt code]
These are exactly the same as the SYSCALL actions, except for the three values pushed after the 16 registers. These are information that may be needed to correctly handle the interrupt.
Note that if the interrupt handler behaves like a normal function, and performs PUSH FP and LOAD FP, SP as its first actions, then those three pieces of information will be available at [FP+3], [FP+4], and [FP+5], the locations of the first three parameters to a function in BCPL.
let interrupt_handler(intcode, address, info) be
{
ireturn; }
The first parameter is always the interrupt code, the IV$ value for the interrupt. For the following interrupts:
PAGEFAULT, PAGEPRIV,
the second parameter is the virtual address that caused the problem.
For this interrupt:
the second parameter is the physical address that caused the problem.
For the following interrupts:
UNIMPOP, HALT, DIVZERO, UNWROP, PRIVOP, BADCALL, DEBUG,
the second parameter is the address of the instruction that caused the problem (i.e. PC value).
MEMORY,
For this interrupt:
the third parameter is the operand of the SYSCALL instruction that caused the problem.
For this interrupt:
INTRFAULT,
which is only caused by a fatal error during interrupt processing, the second parameter is
left unchanged from the original interrupts setting, and the third parameter is set to the interrupt code for the original interrupt.
Realise that if each process has its own system stack, then each process must also have its own value for the system stack pointer, which must be saved and restored when processes are switched.
BADCALL,
INPUT AND OUTPUT OPERATIONS
All interactions with any hardware outside of the CPU are controlled by the PERI instruction. There are four general groups of IO operations supported:
Disc Operations: These allow direct access to the emulated disc drives, permitting whole blocks (128 words, which is the same size as 512 bytes) to be transferred between memory and a specified location on the disc. These operations are necessary for file-system implementation.
Magnetic Tape Operations: These provide a realistic way of accessing files in the real (i.e. outside the emulator, probably unix) file system. Without these it would be very difficult and time consuming to get useful test data into your own file system implementations.
Terminal Operations: These allow characters to be read from the controlling keyboard or written to appear on the monitor.
Time Operations: reading the emulated hardware clock and telling you the date and time.
All IO operations are controlled in the same way. A small lump of memory is filled with information describing the operation to be performed, and with space to receive the results. The PERI instruction sends these few words to the appropriate piece of hardware. When the operation is complete, data returned by the hardware, if any, is stored back into the small lump of memory, a success-or-error code (zero or positive for success, negative for failure) is put into the instructions main register, and execution continues. The ERR flag is also cleared for success and set for failure.
Example: Finding the total size of disc drive number one.
The DISCCHECK IO operation requires a two-word control structure. All IO control structures must have the required operation code, in this case $DISCCHECK, stored in the first word. This particular operation also requires the second word to contain the disc drive number.
LOAD R2, control LOAD R1, $DISCCHECK STORE R1, [R2]
LOAD R1, 1
STORE R1, [R2+1] PERI R3, control JCOND ERR, failed etc
control:
.SPACE 3
If the operation is not successful, the ERR flag will be set, and the program will jump to the failed: label to deal with the situation, and R3 will contain a negative number as an error code. If the operation is successful, then R3 will contain the total number of blocks in disc number 1.
Of course, control structures may be set up in advance, like this:
PERI R3, control JCOND ERR, failed etc
control:
.DATA 1
.DATA $SIZEDISC
This style requires fewer instructions, but is slightly less flexible.
PERI is a privileged operation, and can not be executed in user mode.
If the operation code is not recognised, nothing happens except that the error code 1 (ERR_BAD_CODE) is stored in the register and the ERR flag is set.
DISC OPERATIONS
Disc drives are set up at system initialisation. The system.setup file describes the disc drives that are needed. An example line from system.setup is disc 1 maindrive 6000, it means that disc drive number 1 should be at least 6000 blocks long, and will actually be kept in the real file maindrive.disc. If such a file does not exist, it is created. If the file does exist, it is used as-is. The size of maindrive.disc will of course be 6000*512 bytes. The disc file is not actually created until it is first accessed, and even then it is only made big enough to store the blocks that have so far been written. Reading from a block that has never been written is not an error.
$DISCCHECK
Requires a 2 word control structure, as follows
0: the value $DISCCHECK
1: disc drive number
Error codes (returned in register):
2, ERR_READ_PARAMS: memory access problem reading the control structure. 3, ERR_DEV_NUMBER: drive number < 1 or > 8.
Successful result (returned in register): disc size, in blocks, or
0 if the indicated disc does not exist. $DISCREAD
Requires a 5 word control structure, as follows
0: the value $DISCREAD
1: disc drive number
2: (disc address) the number of the first block to be read
3: the number of consecutive blocks to be read
4: (memory address) the address into which the data should be stored.
make sure that there are at least (number of blocks * 128) words of space there.
Error codes (returned in register):
2, ERR_READ_PARAMS:
3, ERR_DEV_NUMBER:
4, ERR_POSITION:
5, ERR_MEMORY:
memory access problem reading the control structure. indicated disc not available
attempt to read a block number < 0 or >= size. memory access problem reading the data
Successful result (returned in register):
number of blocks transferred from disc to memory.
$DISCWRITE
Requires a 5 word control structure, as follows
0: the value $DISCWRITE
1: disc drive number
2: (disc address) the number of the first block to be written
3: the number of consecutive blocks to be written
4: (memory address) the address of the data to be written.
make sure that there are at least (number of blocks * 128) words of space there.
Error codes same as for $DISCREAD plus
6, ERR_DEV_FAILED: real failure to write all the data
Successful result (returned in register):
number of blocks transferred from memory to disc.
MAGNETIC TAPE OPERATIONS
Real files in the outside operating system are made available in the guise of magnetic tapes. To access a real file, a program must first load that file onto a tape drive. It may then either read from the file sequentially in units of 128 word blocks, or it may write units of 128 word blocks into the file. Finally, the tape drive must be unloaded. Files/tapes are automatically rewound to the beginning when they are loaded.
Magnetic tape drives are referred to by their unit number in the range 1 to 8. All blocks on a tape must be exactly 128 words (512 bytes), except that the last one may be smaller because they correspond to real files whose sizes are fixed.
$TAPECHECK
Requires a 2 word control structure, as follows
0: the value $TAPECHECK
1: tape unit number
Error codes (returned in register):
2, ERR_READ_PARAMS: memory access problem reading the control structure. 3, ERR_DEV_NUMBER: drive number < 1 or > 8.
Successful result (returned in register): R if the tape is readable,
W if the tape is writable, or
0 if the indicated tape has not been loaded.
$TAPEREWIND
Requires a 2 word control structure, as follows
0: the value $TAPEREWIND
1: the tape unit number
Error codes (returned in register):
2, ERR_READ_PARAMS: memory access problem reading the control structure. 3, ERR_DEV_NUMBER: tape unit not available.
Successful result (returned in register): 1
$TAPELOAD
Requires a 4 word control structure, as follows
0: the value $TAPELOAD
1: the tape unit number
2: pointer to a string containing the real file name on the host system
3: mode, either R for read only or W for write only
Error codes (returned in register):
2, ERR_READ_PARAMS:
3, ERR_DEV_NUMBER:
5, ERR_MEMORY:
7, ERR_NOT_FOUND:
8, ERR_BAD_PARAM:
memory access problem reading the control structure. tape unit not available.
memory access problem reading the filename string the file is not accessible.
mode is neither R nor W. Successful result (returned in register):
1 $TAPEUNLOAD
Requires a 2 word control structure, as follows
0: the value $TAPEUNLOAD
1: the tape unit number
Error codes (returned in register):
2, ERR_READ_PARAMS:
3, ERR_DEV_NUMBER:
7, ERR_NOT_FOUND:
memory access problem reading the control structure. tape unit not available.
tape unit was not loaded.
Successful result (returned in register): 1
$TAPEREAD
Reads the next block from tape into memory
Requires a 3 word control structure, as follows
0: the value $TAPEREAD
1: tape unit number
2: (memory address) the address into which the data should be stored.
make sure that there are at least 128 words of space there.
Error codes (returned in register):
2, ERR_READ_PARAMS:
3, ERR_DEV_NUMBER:
5, ERR_MEMORY:
memory access problem reading the control structure. tape unit not available
memory access problem reading the data
Successful result (returned in register):
number of bytes transferred from tape to memory, or 0 if the end of the tape had already been reached.
$TAPEWRITE
Requires a 4 word control structure, as follows
0: the value $TAPEWRITE
1: tape unit number
2: (memory address) the address of the data to be written.
3: the number of bytes to be written
Error codes same as for $DISCREAD plus
6, ERR_DEV_FAILED: real failure to write all the data
Successful result (returned in register):
number of bytes transferred from memory to tape.
Example: Reading the first 512 characters from a real unix file and displaying them.
LOAD R1, control LOAD R2, $TAPELOAD STORE R2, [R1] LOAD R2, 1
STORE R2, [R1+1] LOAD R2, filename STORE R2, [R1+2] LOAD R2, R
STORE R2, [R1+3] PERI R3, control JCOND ERR, failed
// unit number
// READ ONLY
// have the tape loaded
LOAD R2, $TAPEREAD STORE R2, [R1] LOAD R2, 1
STORE R2, [R1+1] LOAD R2, space STORE R2, [R1+2] PERI R3, control JCOND ERR, failed
// unit number
// where to put those characters // read from the tape
// number of characters
// where those characters are // print
// unit number
// close the real file
LOAD R2, STORE R2, LOAD R2, STORE R2, LOAD R2, STORE R2, PERI R3,
LOAD R2, STORE R2, LOAD R2, STORE R2, PERI R3, HALT
$TERMOUTC [R1] 512 [R1+1] space [R1+2] control
$TAPEUNLOAD [R1]
1
[R1+1] control
filename:
.STRING tests/file.txt
control: .SPACE 4
space:
.SPACE 128
TERMINAL OPERATIONS
There are two essential operations: read a bunch of characters from the keyboard and write a bunch of characters to the screen. The read function is compatible with interrupt-driven user input: when a program is running (not just single stepping) and interrupts are enabled, every time a keyboard key is pressed its ASCII code is added to the end of the hardware keyboard buffer and a $KEYBD interrupt is signalled. The $TERMINC operation takes characters from the beginning of the hardware keyboard buffer.
Character codes are available as soon as the key is pressed, the system does not wait until a whole line is available. This means that any special behaviour associated with particular keys (such as ENTER or BACKSPACE) must be programmed. The one exception is control-c; that will always interrupt a running program and return to single stepping mode.
$TERMINC
Requires 3 word control structure, as follows
0: the value $TERMINC
1: the maximum number of characters to be read
2: (memory address) the address into which the characters should be stored.
make sure that there are at least ((maximum number + 1) / 4) words of space there.
Error codes (returned in register):
2, ERR_READ_PARAMS: memory access problem reading the control structure. 5, ERR_MEMORY: memory access problem storing the characters
Successful result (returned in register): number of characters actually read.
Notes:
It is not an error to attempt to read when the keyboard buffer is empty.
If no characters are already in the keyboard buffer, it will not wait for input.
The characters received are packed four per word to make a proper string, and that
string will be zero terminated. Strings are organised so that the first character goes in the least-significant bits of the first word. This means that if just a single character is read, the first word of the result will simply be its ASCII code.
Any characters left unread in the buffer will be received by the next TERMINC. $TERMOUTC
Requires 3 word control structure, as follows
0: the value $TERMOUTC
1: the number of characters to be printed
2: (memory address) the address at which the characters may be found.
Error codes are the same as for $TERMINC Successful result (returned in register):
the number of characters actually printed
Notes:
The characters to be printed should be in the form of a proper string (packed four
per word) starting at the given memory location. The string does not need to be
zero-terminated.
If the number of characters is specified to be zero, the string will be assumed to be
zero-terminated, and an unlimited number of characters will be printed.
If the number of characters is specified to be non-zero, that number of characters
will be printed, even if they include some zeros.
If the number of characters is specified to be one, then the memory location may
just contain the characters ASCII code; no extra formatting is required to make it into a string.
$TERMINW and $TERMOUTW
These operations perform exactly as TERMINC and TERMOUTC with the following exceptions:
The data is not formatted as a string. Reading or printing N characters requires exactly N words of memory, containing one ASCII code each.
The input operation will not zero-terminate the array of characters.
TIME OPERATIONS $SECONDS
Requires 1 word control structure, as follows 0: the value $SECONDS
Error codes:
2, ERR_READ_PARAMS: memory access reading or writing the control structure.
Successful result (returned in register):
The number of seconds elapsed since midnight (0000 hours) on 1st January 2000.
$USECONDS
Requires 3 word control structure, as follows
0: the value $SECONDS
1: output only: receives the number of seconds
2: output only: receives the number of microseconds
Error codes:
2, ERR_READ_PARAMS: memory access reading or writing the control structure.
Successful result (returned in the control structure):
The number of microseconds elapsed since midnight (0000 hours) on 1st January 2000, split into separate integers for seconds and microseconds.
$DA TETIME
Splits a date/time value into its human-oriented parts.
Requires 9 word control structure, as follows
0: the value $DATETIME
1: a time value of the kind returned by $SECONDS
2: output only: receives the year
3: output only: receives the month, 1 to 12
4: output only: receives the day of the month, 1 to 31
5: output only: receives the day of the week, 0 to 6, 0 = Sunday
6: output only: receives the hour, 0 to 23
7: output only: receives the minute, 0 to 59
8: output only: receives the second, 0 to 59
Error codes:
2, ERR_READ_PARAMS: memory access reading or writing the control structure.
VIRTUAL MEMORY
Because the emulator uses 32 bit words instead of 8 bit bytes, the Intel scheme of splitting a virtual address into a 10 bit page table number, a 10 bit page number, and a 12 bit offset can not be used exactly.
A 12 bit offset means that there would be 4096 memory locations in a page, and that would mean that a page table could hold the addresses of 4096 pages instead of 1024, so we would not need so many of them.
In the emulator a page of memory consists of 2048 32-bit locations requiring only an 11 bit offset. That means that a page table can hold the addresses of 2048 pages, so 11 bits are required for page numbers. That leaves only 10 bits for the page table number, meaning that page directories only fill half a page.
A Virtual Address
There are two advantages to this changed layout: pages are smaller, so more of them are available without using up so much real memory, and page directories only fill half a page, so it is quite possible that you can store everything you need to know about a process in one single page.
Only the most significant 22 bits of the value stored in the Page Directory Base Register are looked at during virtual address translation. Page directories must occupy complete half-pages; their addresses must be multiples of 1024 (i.e. in binary they must end in 10 zeros).
Only the most significant 21 bits of the values stored in the Page Directories are looked at during virtual address translation. Page tables must occupy whole pages; their addresses must be multiples of 2048 (i.e. in binary they must end in 11 zeros).
The entries in page tables include two page status bits in the least significant bits. They are the Resident or Valid bit (in bit 0) and the System bit (in bit 1). The meaning of a page table entry depends upon the value of the Resident bit.
A Page Table Entry, which is the same as a Page Directory Entry.
Page Table Number
Page Number
Offset
Physical Page Address
Unassigned
S
R
00 11 22 33 44 55 66 77 88 99
10 10
11 11
12 12
13 13
14 14
15 15
16 16
17 17
18 18
19 19
20 20
21 21
22 22
23 23
24 24
25 25
26 26
27 27
28 28
29 29
30 30
31 31
If the Resident bit is Zero, any access to this virtual page will immediately cause a PAGEFAULT interrupt, and the other 31 bits will not even be seen. They may be used for any purpose whatsoever.
If the System bit is 1, any attempted access to this virtual page while in User mode will result in a PAGEPRIV interrupt, and the access will not occur.
In all cases, bits 2 to 10 have no assigned meaning, and may be used for any purpose whatsoever.
MEMORY ACCESS ALGORITHM
let A be the address in memory referenced by an instruction. if $VM flag is OFF:
Use physical memory at address A otherwise, if $VM flag is ON:
// A is a virtual address and will be translated.
let DIR be (A>>22)&0x3FF
let PG be (A>>11)&0x7FF
let OFFS be A&0x7FF
let POS be DIR + contents of $PDBR register read PTADDR from physical memory address POS if PTADDR is Zero:
// most significant 10 bits // next 11 bits
// least significant 11 bits
PAGEFAULT, translation abandoned
PTADDR &= 0xFFFFF800 // zero out least significant 11 bits read PGADDR from physical memory address (PG + PTADDR)
let R be PGADDR & 1 // least significant bit
if R is Zero:
PAGEFAULT, translation abandoned
let S be (PGADDR & 2)>>1 // second least significant bit if S is One and $SYS flag is Zero:
PAGEPRIV, translation abandoned
PGADDR &= 0xFFFFF800 // zero out least significant 11 bits let PHYS be PGADDR + OFFS
Use physical memory at address PHYS
PRIVILEGED OPERATIONS
If any of the following instructions are executed when the $SYS flag is off (zero), a PRIVOP interrupt will be triggered and the operation will not be performed.
SETSR, PERI, IRET, PHLOAD, PHSTORE, FLAGSJ, CLRPP
If a HALT instruction is executed when the $SYS flag is off (zero), a HALT interrupt will be
triggered and the processor will not be halted.
If a SETFL instruction is executed when the $SYS flag is off (zero), and it attempts to modify either the R, SYS, VM, or IP flag, a PRIVOP interrupt will be triggered and the operation will not be performed.
Reviews
There are no reviews yet.