To avoid dealing with the mind-numbing array of brain-damaged gotchas real computer architectures lay in the OS implementor's path, the hardware architecture described here is a combination of generic computer-architecture features; the whole is not representative of any real computer architecture. However, some architectural features, particularly the idea of making everything accessible by mapping into Primary Store, are based on Digital Equipment Corp.'s (DEC's, now Compaq's) PDP series of minicomputers.
There are four hardware components in the architecture: the CPU, the Primary Store, a disk, and a terminal. Of these four components, the Primary Store can be thought of as being the main component because all other components in the architecture are accessible through Primary Store.![]()
Primary Store is represented as an array of words. The index associated with each word is also known as the word's address. The arrays are indexed as they are in C: starting from zero and increasing by 1 for each successive array element.
Primary Store is partitioned into three contiguous subarrays known as the the System Space, the User Space, and the Device Space:
These three areas are called "spaces" to emphasize that they are part of Primary Store.![]()
Each space is defined by three constants: s_base
, s_base
,
and s_base
, where s is one of sys
, usr
, or dev
.
The constant values represent
_base
: The lowest accessible address in the associated space.
_size
: The number of words in the associated space.
_top
: One more than the highest accessible address in the
associated space; that is s_top
= s_base
+ s_size
.
sys_base
is the lowest accessible address in Primary Store and
dev_top
is one more than the highest accessible address in Primary Store.
Note also that sys_top == usr_base
and usr_top == dev_base
.
User Space is divided into 32 page frames, where each page frame consists of 32 consecutive words of user space. Page frames are numbered from 0 to 31 with page frame 0 starting at Primary Store address 1024 and page frame 31 ending just before address 2048.
The hardware uses the contents of five of the CPU registers to control various aspects of process execution; the hardware ignores the contents of the remaining eleven registers. The arrangement of registers in Primary Store is
The five registers used by the hardware are (the constant name in parenthesis is the address of the register)![]()
base_register
) top_register
) ia_register
) pc_register
) ps_register
) Bits are numbered from right to left in a word; bit 0 is the rightmost bit in a word.
The constant![]()
halt_register
gives the Device-Space addresses of the Halt
Register.
Writing a value, any value, into the Halt Register ends simulator execution.
pages_in_user_space
elements; each array
element of the page table is a
page-table entry. The i-th element
in the page table corresponds to the i-th page fame in user space. The
fields in a page-table entry are
All fields can be manipulated by the operating system. The hardware also
automatically sets accessed_at
and modified_at
on each reference to
valid page-table entries.
disk_size
gives the number of
blocks in a disk. The size of each disk block is also fixed; all disk blocks
in a disk have the same size. The constant disk_block_size
gives the
number of words in a disk block.
A disk is controlled through a set of four disk registers mapped into a group of four words in Device Space:
The constants![]()
disk_command_register
, disk_block_register
, disk_address_register
, and disk_status_register
give the
Device-Space addresses of the associated disk registers.
The function of each disk register is (the constant name in parenthesis is the Device-Space address of register)
disk_command_register
)
Writing the value device::read
to the Disk Command Register initiates
disk input: the disk block at the address contained in the Disk Block Register
is moved from the disk to Primary Store starting at the address contained in
the Disk Address Register.
Writing the value device::write
to the Disk Command Register initiates
disk output: the data from the sequence of words starting at the address given
in the Disk Address Register and continuing through the size of a disk block is
copied from Primary Store and written to the disk block at the address
contained in the Disk Block Register.
Any value other than device::read
or device::write
written to the
Disk Command Register is interpreted as an illegal command code.
disk_block_register
)disk_address_register
)disk_status_register
)status::ok | command completed successfully |
status::bad_command | unrecognized command |
status::bad_block_number | bad disk-block number |
status::bad_memory_address | bad primary-store address |
The constants![]()
terminal_command_register
, terminal_data_register
, and terminal_status_register
give the Device-Space
addresses of the associated terminal registers.
The function of each terminal register is (the constant name in parenthesis is the Device-Space address of register)
terminal_command_register
)
Writing the value device::read
to the Terminal Command Register
initiates terminal input: the next 8-bit character to arrive from the terminal
is stored in byte 0 of the Terminal Data Register.
Writing the value device::write
to the Terminal Command Register
initiates terminal output: the contents of byte 0 in the Terminal Data Register
is sent to the terminal.
A terminal-io command begins as soon as a command is written to the command register, using the value contained in the data registers (if writing). Once a command is started, writing a new value into the data register has no effect on the in-progress operation.
Any value other than device::read
or device::write
written
to the Terminal Command Register is interpreted as an illegal command code.
terminal_data_register
)terminal_status_register
)status::ok | command completed successfully |
status::bad_command | unrecognized command |
vmem_base
(inclusive) to vmem_top
(exclusive). A
process need not occupy all its process address space; if it does not, then any
attempt to access an address in the unoccupied area of the process address
space should cause an invalid address interrupt.
A process address space is broken up into 64 pages, where each
page is
a contiguous sequence of 32 words. The pages are numbered from 0 to 63, where
page 0 contains address vmem_base
and page 63 contains address
vmem_top
- 1.
Given an 11-bit virtual address va
, the leftmost 6 bits of va
determine the
page number associated with va
; the rightmost 5 bits
of va
determine the
page offset within the page at which the word
addressed by va
lies.
The physical process-address space comprises the 1024 words making up
User Space. The valid addresses in a physical process address space
range from usr_base
(inclusive) to usr_top
(exclusive). A process
need not occupy all its physical process address space; if it does not, then
any attempt to access an address in the unoccupied area of the physical process
address space should cause an invalid address interrupt.
A physical address is interpreted as the 10-bit address of a word in User Space and is not further broken down into component values.
va
issued by the CPU is run through the Memory Management Unit (MMU) to
determine where - or if - the page holding va
resides in user space.
Upon receiving the virtual address va
, the MMU looks for a
process-table entry having the following characteristics:
va
matches page_number
.
If such a page-table entry exists, the MMU translates va
into a physical
address in User Space; otherwise the MMU throws an invalid address
interrupt. When the MMU throws an invalid address interrupt, the PC is reset
to point to the instruction containing the invalid address.
If a user process is not executing under virtual memory, then every address issued by the CPU is used to access user space directly without translation.
There are seven interrupts defined. Different interrupts cause different code
to be executed under system mode. The seven interrupts defined are (the
constant name in parenthesis is the offset from next_register
)
illegal_instruction_i
)reboot_i
)system_call_i
)invalid_address_i
)disk_i
)terminal_i
)countdown_i
)The interrupt vector is a set of seven words in System Store that gives the addresses of the code that executes when an interrupt occurs. The interrupt vector occupies storage just above the register set:
The contents of each word is interpreted as the starting address of the code which will be executed when the associated interrupt occurs.![]()
A device begins an operation as soon as a value is written into its Command Register. The device copies the values stored in the argument registers into internal registers at the start of the operation; the argument registers may be rewritten without effecting the operation in progress.
The appropriate interrupt (disk or terminal) is raised when the operation in progress ends. The contents of the Status Register for the operation in progress is undefined until the interrupt is raised. Writing a value into a Command Register always results in an interrupt, even when the status indicates an error.
Writing a value into a Command Register always starts a new operation. If an operation is in progress when a value is written into the Command Register, it is cancelled and a new operation is started. This behavior occurs whether or not the new operation succeeds or fails.
The system calls that should be supported by the operating system are (the constant in parenthesis is the value in register 0):
system_call::exit
)system_call::put_msg
)After the put-message system call returns, register 0 contains the system call status:![]()
status::ok | command completed successfully |
status::message_pool_full | there is no space left in the message pool to store new messages |
The contents of registers 1 and 2 remain unchanged after the system call returns.
system_call::(<code>system_call::get_msg</code>)
)After the put-message system call returns, register 1 is unchanged and register 2 contains the message data from a message having a tag equal to the tag given in register 1; the matching message is removed from the message pool. Register 0 always contains the value![]()
status::ok
.
If no message in the message pool has a tag that matches the contents of register 1, the calling process blocks until such a message appears in the pool. If more than one message in the message pool has a matching tag, any one of the matching messages may be selected. Message selection should be fair, that is, if a message m in the pool has tag t and a continual sequence of processes try to get a message with tag t, then m should eventually be matched.
A message can be matched by a process at most once. If more than process is blocked waiting for a message, than any one of the blocked processes may be selected once the message is placed in the pool. Process selection should also be fair, that is, if a process p is blocked waiting for a message and a continual sequence of matching messages are put in the pool, then p should eventually match a message and unblock.
system_call::open
)device::disk
and device::terminal
. The status of the open system
call is returned in CPU register 0; the possible status codes are
After the open system call is completed, CPU register 0 contains the system call status:![]()
status::ok | command completed successfully |
status::device_busy | requested device has already been allocated |
status::bad_device | the value d in CPU register 1 is not device::disk
or device::terminal |
system_call::close
)After the close system call is completed, CPU register 0 contains the system call status:![]()
status::ok | command completed successfully |
status::bad_device | the device id in CPU register 1 is not associated with any device |
system_call::read
)After the read system call is completed, CPU register 0 contains system call status:![]()
status::ok | command completed successfully |
status::bad_device | the device id in CPU register 1 is not associated with any device |
status::bad_address | the Primary Store address in CPU register 2 is bad |
status::bad_count | the contents of CPU register 3 is bad |
system_call::write
)After the write system call is completed, CPU register 0 contains the system call status:![]()
status::ok | command completed successfully |
status::bad_device | the device id in CPU register 1 is not associated with any device |
status::bad_address | the Primary Store address in CPU register 2 is bad |
status::bad_count | the contents of CPU register 3 is bad |
system_call::open
)device::file
. Register 3 contains the integer identifying the file to
open. Register 2 contains a set of flags indicating how the file should be
opened:
file_io::read
file_io::write
file_io::create
file_io::exists
file_io::exclusive
file_io::shared
The flag set may contain both reading and writing; if neither are given, reading is assumed. The flag set may contain both create and exists; if neither are given, exists is assumed. The flag set may contain either exclusive or shared, but not both; if neither is given, shared is assumed.
After the open system call is completed, CPU register 0 contains the system call status:![]()
status::ok
device::device_busy
device::bad_device
device::disk
device::terminal
, or device::file
.
If the open system call completed successfully, CPU register 1 contains a file identifier, which must be included as an argument to other system calls related to this file. If the open system call didn't complete successfully, the contents of CPU register 1 is undefined.
system_call::close
)After the close system call is completed, CPU register 0 contains the system call status:![]()
status::ok
status::bad_device
If the close system command completes successfully, the file id held by the user process becomes invalid and may not usefully be used in any other file-oriented system call.
system_call::read
)After the read system call is completed, CPU register 0 contains system call status:![]()
status::ok
status::bad_device
status::bad_address
status::bad_count
If the read system command completes successfully, CPU register 1 contains the number of words read. The number of words actually read may be smaller (never larger) than the number of words requested if the number of words from the file pointer to the end of the file is less than the requested number of words to read.
system_call::write
)After the write system call is completed, CPU register 0 contains the system call status:![]()
status::ok
status::bad_device
status::bad_address
status::bad_count
system_call::seek
)'c'
, 'b'
, or 'e'
:
'c'
: Offset from the current file pointer location. If
the file pointer is pointing at word p in a file, and register 3
contains i, then the file pointer will be pointing at the word p +
i after the seek call is done.
'b'
: Offset from the beginning of the file. If register
3 contains i, then the file pointer will be pointing at the word i
after the seek call is done.
'e'
: Offset from the end of the file. If the file
contains n words and register 3 contains i, then the file pointer
will be pointing at the word n - i - 2 after the seek call is done.
It is not an error to attempt to move the file pointer past either end of a file; in such cases, the file pointer moves as far as it can and stops.
After the write system call is completed, CPU register 0 contains the system call status:![]()
status::ok
status::bad_device
status::bad_address
'c'
, 'b'
, or
'e'
.
Register 1 contains the number of the word being referenced by the file pointer.
system_call::remove
)After the write system call is completed, CPU register 0 contains the system call status:![]()
status::ok
status::bad_device
A file that's opened can be removed; the remove should be delayed until all opens on the file are closed. A file that's undergoing delayed deletion can be opened by other processes.
This page last modified on 24 November 2001.