Simulation instructions ----------------------- T 1 /row want-head /mark 1 /table want-loose-left /mark $ /row want-red /del /mark t -------- ---- --------------------- -------------------------------- Opcode P/U Category Description `CLOCK` user simulation get date and time `CLOSE` user simulation close simulator file handle `DEC` user simulation write unsigned integer in decimal `GETC` user simulation read 8 bits from file `GETW` user simulation read 36 bits from file `HALT` priv$ simulation halt `HEX` user simulation write integer in hexadecimal `LENC` user simulation get length of file in bytes `LENW` user simulation get length of file in words `MEMSET` priv$ simulation fill block of memory `NSLEEP` priv$ simulation pause for simulated nanoseconds `OCT` user simulation write integer in octal `OPENR` user simulation open paravirtualized file for reading `OPENM` user simulation open multiplexed tty for writing `OPENW` user simulation open paravirtualized file for writing `PUTC` user simulation write 8 bits to file `PUTW` user simulation write 36 bits to file `SDEC` user simulation write signed integer in decimal `SETEOF` user simulation set end-of-file indication word `SETIDL` user simulation set nothing-to-read indication word `TET` user simulation write integer in tetrasexagesimal `UNLINK` user simulation delete paravirtualized file Simulation opcodes are implemented by the electrical simulation (in `netsim/comps/trap.c`) for diagnostic and development purposes. Real-life Dauug|36 systems won't support any of these opcodes, but instead treat all of them as no-ops. A few of these opcodes are treated as privileged by Osmin because user programs can easily harm other programs or system integrity by using them. Most of the simulation opcodes implement a paravirtualized filesystem. This filesystem has no concept of directories or privileges; instead, filenames are just 36-bit integers that map into the host filesystem via their tetrasexagesimal representations. So the assembly language filename $dauug`t$ becomes $file‑0dauug$ within the host filesystem. This means that "user" programs within an Osmin simulation have full access to each other's files. That's ok for testing, but it won't apply to real-life computers. `$ No CPU flags change - - - - - - - The simulation opcodes are implemented by intercepting control decoder reads, and then directly accessing the code RAM and registers. This electrically looks like a no-op, and one consequence is that simulation opcodes cannot modify any CPU flags. So there will be places where assembly language syntax may look like you can test a flag, but you can't. For example: a signed fd fd = openr MyFile`t ; will return all 1s if open fails jump < noSuchFile needs an additional instruction to update flags, such as a signed fd fd = openr MyFile`t fd = fd + 0 jump < noSuchFile Many programs probably won't bother having `fd` be a signed register, in which case they would a unsigned fd fd = openr MyFile`t ; all 1s if open fails fd = fd | fd ; bitwise OR copies bit 35 to N flag jump < NoSuchFile Some syntax is quirky - - - - - - - Because Dauug|36 assembly language has no reserved words, statements must adhere to some rigid patterns. This adherence can be subtly misleading. For example, in a putc fd = 32 one would think `fd` is a destination register because it's left of the `=` sign. Actually, `putc` does not modify any register, so `fd` and `32` are `putc`'s left and right operands. The above statement can be read to essentially mean "the byte being output to stream `fd` is 32." For example code - - - - - - Any `.a` file under the `netsim` directory that contains `openm`, `openr`, or `openw` is a good candidate for study. `CLOCK` Get date and time - - - - T 1 /row want-head /mark 1 /table want-loose-left /mark t - Syntax `now = clock tz` t - - Register · Signedness `now` · signed `tz` · signed · 1 opcode only t - No flags changed `CLOCK` obtains the number of seconds since the Epoch from the host operating system by calling `time(NULL)`. To this integer, it adds the signed value `tz` and returns the result in `now`. Because Dauug|36 has 36-bit words, all time between 882 C.E. and 3058 C.E. are representable, plus a bit outside this range. In other words, this architecture has a "year 3059 problem." The assembler does not enforce signedness for this opcode. `CLOSE` Close simulator file handle - - - - T 1 /row want-head /mark 1 /table want-loose-left /mark t - Syntax `close fd` t - - Register · Signedness fd · ignored · 1 opcode only t - No flags changed `CLOSE` closes the file or multiplexed tty associated with an open handle. No registers are changed. No indication is made in the event of an error. `DEC` Write unsigned integer in decimal - - - - T 1 /row want-head /mark 1 /table want-loose-left /mark t - Syntax `DEC fd = num` t - - Register · Signedness fd · ignored num · unsigned · 2 opcodes (see also `SDEC`) t - No flags changed `DEC` writes the unsigned right operand `num` in decimal to open file handle `fd`. No registers are changed. No indication is made in the event of an error. The `DEC` and `SDEC` opcodes both use `DEC` as their mnemonic. The assembler will determine which opcode to produce from the signedness of `num`. The number output opcodes `DEC`, `HEX`, `OCT`, and `SDEC` do not output spaces, separators, or leading zeros. In contrast, `TET` outputs six base-64 digits and preserves leading zeros. `GETC` Read 8 bits from file - - - - T 1 /row want-head /mark 1 /table want-loose-left /mark t - Syntax `c = getc fd` t - - Register · Signedness c · ignored fd · ignored · 1 opcode only t - No flags changed `GETC` reads one byte from open file handle `fd`. Ordinarily, the result will be in the range 0 through 255 and placed in `c`. Simulation reads are performed with nonblocking Linux system calls. If there is no data to read, a 36-bit "idle" value is returned. See also `SETIDL`. If the read does not complete due to end-of-file, a 36-bit "eof" value is returned. See also `SETEOF`. If the read does not complete due to an error, the all-ones value is returned. Most likely, the error will be that `fd` is not an open file handle. `GETW` Read 36 bits from file - - - - T 1 /row want-head /mark 1 /table want-loose-left /mark t - Syntax `c = getw fd` t - - Register · Signedness c · ignored fd · ignored · 1 opcode only t - No flags changed `GETW` reads five bytes from open file handle `fd` as a 40-bit number in big-endian order (most-significant byte first). The top four bits are discarded, and the 36 least significant bits are placed in `c`. Simulation reads are performed with nonblocking Linux system calls. If `GETW` would block before five bytes are read, a 36-bit "idle" value is returned. The input stream is positioned as if the `GETW` did not occur. See also `SETIDL`. If `GETW` would encounter an end-of-file before five bytes are read, a 36-bit "eof" value is returned. The input stream is positioned as if the `GETW` did not occur. See also `SETEOF`. `HALT` Halt - - - - - - T 1 /row want-head /mark 1 /table want-loose-left /mark t - Syntax `halt` t - No registers used t - No flags changed The `HALT` instruction causes the simulator to stop. The active `.ns` test script will continue at the line following the `run` that started the simulator. The simplest use case for `HALT` is that a program has terminated. Another use case is that the `.ns` script can check the value most recently written to the register file to verify that an assembly language program is executing correctly. An `@` SRAM tweak is used for this check (see `netsim/comps/c_sram.c`). After the check, the script can resume the program using a subsequent `run` directive. `HEX` Write unsigned integer in hexadecimal - - - - T 1 /row want-head /mark 1 /table want-loose-left /mark t - Syntax `HEX fd = num` t - - Register · Signedness fd · ignored num · ignored · 1 opcode only t - No flags changed `HEX` writes the unsigned right operand `num` in hexadecimal to open file handle `fd`. No registers are changed. No indication is made in the event of an error. The number output opcodes `DEC`, `HEX`, `OCT`, and `SDEC` do not output spaces, separators, or leading zeros. In contrast, `TET` outputs six base-64 digits and preserves leading zeros. `LENC` Get length of file in bytes - - - - T 1 /row want-head /mark 1 /table want-loose-left /mark t - Syntax `len = lenc fname` t - - Register · Signedness c · signed fname · unsigned · 1 opcode only t - No flags changed `LENC` takes tetrasexagesimal filename `fname` (a 36-bit register with six packed characters) and checks to see if a file with that name exists in the paravirtualized filesystem. If it does, the file's size in bytes is returned in `len`. If the file does not exist or there is an error, −1 is returned. `LENC` works on file names, not on open file handles. It does not matter whether the file is open or not. The assembler does not enforce signedness for this opcode. `LENW` Get length of file in words - - - - T 1 /row want-head /mark 1 /table want-loose-left /mark t - Syntax `len = lenw fname` t - - Register · Signedness c · signed fname · unsigned · 1 opcode only t - No flags changed `LENW` takes tetrasexagesimal filename `fname` (a 36-bit register with six packed characters) and checks to see if a file with that name exists in the paravirtualized filesystem. If it does, the file's size in words is returned in `len`. If the file does not exist or there is an error, −1 is returned. The number of words in a file is considered to be the number of bytes, divided by 5. Any fractional word left over does not count towards the file's size. `LENW` works on file names, not on open file handles. It does not matter whether the file is open or not. The assembler does not enforce signedness for this opcode. `MEMSET` Fill block of memory - - - - T 1 /row want-head /mark 1 /table want-loose-left /mark t - Syntax `memset` t - No registers used t - No flags changed Osmin, like most operating systems, often fill expansive blocks of memory with zeros. Because the electrical simulation may only run a dozen or two instructions per second, these fills take a lot of time. This wouldn't be a problem on a physical computer. `MEMSET` lets Osmin hand off large memory fills to the simulator, which can do the fills lickety-split. It works with several RAMs—both register files, the page table, code memory, and data memory. The SRAMs that participate must all use the `memset` tweak to opt in; any SRAMs without that tweak are immune to `MEMSET`s. `MEMSET` alters the next write operation that will occur on an opted-in SRAM. On that future write, all locations between the previous address written and the new address being written are filled with whatever contents the instruction involved would have written at the new address. Example to fill code memory addresses 1024–2047 with zeros: a wcm 1024 = 0 memset wcm 2047 = 0 Example to fill page 1 of virtual memory with all ones: a sto 4096 = ~1 memset sto 8191 = ~1 The `MEMSET` instruction should be used twice in immediate succession if the intent is to write to a bank of registers. Otherwise, only the left or right copy (whichever electrically happens first) will be `MEMSET`, which is probably not what you want. Example to zero whatever registers happen to be between `alpha` and `omega`: a alpha = 0 memset memset omega = 0 `NSLEEP` Pause for simulated nanoseconds - - - - T 1 /row want-head /mark 1 /table want-loose-left /mark t - Syntax `nsleep duration` t - - Register · Signedness duration · unsigned · 1 opcode only t - No flags changed `NSLEEP` causes the simulation to "freeze" the CPU for the given number of wall-clock nanoseconds. This is more energy-efficient on the host CPU than an idle loop for tests that need to wait, such as an ongoing program that shows the time of day. The longest sleep that can fit in a 36-bit word is about 68.7 seconds. Osmin disallows `NSLEEP` in user programs because they could freeze the system. The assembler does not enforce signedness for this opcode, so `NSLEEP ~1` will sleep for about 68.7 seconds. ~$ `OCT` Write unsigned integer in octal - - - - T 1 /row want-head /mark 1 /table want-loose-left /mark t - Syntax `OCT fd = num` t - - Register · Signedness fd · ignored num · ignored · 1 opcode only t - No flags changed `OCT` writes the unsigned right operand `num` in octal to open file handle `fd`. No registers are changed. No indication is made in the event of an error. The number output opcodes `DEC`, `HEX`, `OCT`, and `SDEC` do not output spaces, separators, or leading zeros. In contrast, `TET` outputs six base-64 digits and preserves leading zeros. `OPENM` Open multiplexed tty for writing - - - - T 1 /row want-head /mark 1 /table want-loose-left /mark t - Syntax `fd = OPENM ttyname` t - - Register · Signedness fd · signed ttyname · unsigned · 1 opcode only t - No flags changed Osmin tests call for several open terminals to display output from concurrently running programs and the kernel. Netsim provides two Python scripts `mux` and `rx` to easily configure and manage these terminals. The `mux` script should run in its own terminal and connects it all together. Invoke it as: p $ ./mux For each terminal you want, also run an instance of `rx` in its own window. Each invocation takes a tetrasexagesimal tty name of up to six digits. For instance: p $ ./rx Jo ; base 64 is case-sensitive $ ./rx walter `OPENM` connects netsim to these scripts by opening a multiplexed terminal `ttyname` and saving its file handle to `fd`. Only writing is supported; there is no way to read from a multiplexed terminal as of August 2025. (You can use `OPENR` via a symlink to write interactive programs.) In assembly language: a unsigned j.fd w.fd j.fd = openm Jo`t w.fd = openm walter`t putc j.fd = 74 ; write capital J to the Jo tty putc w.fd = 119 ; write lowercase w to the walter tty close j.fd close w.fd The assembler does not enforce signedness for this opcode. `OPENR` Open paravirtualized file for reading - - - - T 1 /row want-head /mark 1 /table want-loose-left /mark t - Syntax `fd = openr fname` t - - Register · Signedness fd · signed fname · unsigned · 1 opcode only t - No flags changed `OPENR` takes tetrasexagesimal filename `fname` (a 36-bit register with six packed characters) and checks to see if a file with that name exists in the paravirtualized filesystem. If it does, the file is opened for reading and its handle is written to fd. If the file does not exist or there is an error, −1 is returned. After `OPENR` returns a non-negative file descriptor `fd`, the opcodes you can use with `fd` are `CLOSE`, `GETC`, `GETW`, `SETEOF`, and `SETIDL`. Paravirtualized files all live in a single directory that is determined by an `iodir` directive in the `.ns` script. Within that directory, their names have the format `file-123abc`, where the last six characters are a tetrasexagesimal name. (To review, characters are 0–9, a–z, A–Z, apostrophe, and period.) For example, a paravirtualized filesystem might have this hierarchy: p io/ io/file-000pts io/file-000api io/file-0clock io/file-0color io/file-00echo io/file-0groot io/file-0user1 io/file-bootMe io/file-unlink The `.ns` script to access this filesystem would include the line `iodir io`, assuming netsim is run from the directory that contains `io`. To read from a Linux terminal, have a file within this namespace on the host that symlinks to the device you want. For example, you might p ln -s /dev/pts/0 io/file-000pts The assembler does not enforce signedness for the OPENR opcode. `OPENW` Open paravirtualized file for writing - - - - T 1 /row want-head /mark 1 /table want-loose-left /mark t - Syntax `fd = openw fname` t - - Register · Signedness fd · signed fname · unsigned · 1 opcode only t - No flags changed `OPENW` takes tetrasexagesimal filename `fname` (a 36-bit register with six packed characters) and opens it for writing. If the file already exists, the original contents are preserved and appended to. If the file does not exist, it will be created. If you want to overwrite a file with a brand new one, `UNLINK` the filename before you `OPENW`. `OPENW` will return a handle `fd` for subsequent operations with the file. If the open fails for some reason, `fd` will be −1. The most likely reason for failure is missing an `iodir` directive in the `.ns` script to say where the paravirtualized filesystem lives. After `OPENW` returns a non-negative file descriptor `fd`, the opcodes you can use with `fd` are `CLOSE`, `DEC`, `HEX`, `OCT`, `PUTC`, `PUTW`, `SDEC` (the mnemonic for `SDEC` is `DEC`), and `TET`. If you want `OPENW` to write to a terminal somewhere, use a symbolic link in the file tree (see also `OPENR`). Interactive programs will usually use `OPENR` and `OPENW` on the same filename, but return two different file descriptors. The program `netsim/echo.a` in the August 2025 tarball has a working example of this. The assembler does not enforce signedness for the OPENW opcode. `PUTC` Write 8 bits to file - - - - T 1 /row want-head /mark 1 /table want-loose-left /mark t - Syntax `putc fd = byte` t - - Register · Signedness All · ignored · 1 opcode only t - No flags changed `PUTC` writes the eight least-significant bits of register `byte` to open file handle `fd`. `PUTC` is the natural complement of `GETC`. Exactly one byte is output to the host filesystem. No indication is given if there is an error. The most likely error is that `fd` is not open for writing. The assembler does not enforce signedness for this opcode. `PUTW` Write 36 bits to file - - - - T 1 /row want-head /mark 1 /table want-loose-left /mark t - Syntax `c = a + b` t - - Register · Signedness All · ignored · 1 opcode only t - No flags changed `PUTW` writes 36 bits to the host filesystem as a sequence of five bytes. `PUTW` is the natural complement of `GETW`, so the order is big-endian with four leading zero bits: p 0000zyxw vutsrqpo nmlkjihg fedcba98 76543210 most significance least first output order last No indication is given if there is an error. The most likely error is that `fd` is not open for writing. The assembler does not enforce signedness for this opcode. `SDEC` Write signed integer in decimal - - - - T 1 /row want-head /mark 1 /table want-loose-left /mark t - Syntax `DEC fd = num` t - - Register · Signedness fd · ignored num · signed · 2 opcodes (see also `DEC`) t - No flags changed `SDEC` writes the signed right operand `num` in decimal to open file handle `fd`. No registers are changed. No indication is made in the event of an error. The `DEC` and `SDEC` opcodes both use `DEC` as their mnemonic. The assembler will determine which opcode to produce from the signedness of `num`. The number output opcodes `DEC`, `HEX`, `OCT`, and `SDEC` do not output spaces, separators, or leading zeros. In contrast, `TET` outputs six base-64 digits and preserves leading zeros. `SETEOF` Set end-of-file indication word - - - - T 1 /row want-head /mark 1 /table want-loose-left /mark t - Syntax `seteof fd = word` t - - Register · Signedness All · ignored · 1 opcode only t - No flags changed By default, `GETC` and `GETW` return all ones (either −1 or 68719476735) if a read past the end of a file is attempted. The `SETEOF` instruction lets you change this default for any open file handle `fd` to any 36-bit value `word`. If `SETEOF` changes the end-of-file indicator word to something other than −1, the indicator word for read errors will still be −1. Supporting a configurable error-indicating word is not a priority right now. `SETIDL` Set nothing-to-read indication word - - - - T 1 /row want-head /mark 1 /table want-loose-left /mark t - Syntax `setidl fd = word` t - - Register · Signedness All · ignored · 1 opcode only t - No flags changed By default, `GETC` and `GETW` return almost all ones (either −2 or 68719476734) if a read operation would block. This would happen, for example, for an interactive program that is waiting for a user to type something. The `SETIDL` instruction lets you change this default for any open file handle `fd` to any 36-bit value `word`. `TET` Write integer in tetrasexagesimal - - - - T 1 /row want-head /mark 1 /table want-loose-left /mark t - Syntax `tet fd = num` t - - Register · Signedness All · ignored · 1 opcode only t - No flags changed `TET` writes the unsigned right operand `num` in base 64 to open file handle `fd`. The output will be exactly six digits (selected from 0–9, a–z, A–Z, apostrophe, period). Leading zeros are not suppressed. No registers are changed. No indication is made in the event of an error. `UNLINK` Delete paravirtualized file - - - - T 1 /row want-head /mark 1 /table want-loose-left /mark t - Syntax `unlink fname` t - - Register · Signedness fname · unsigned · 1 opcode only t - No flags changed `UNLINK` takes tetrasexagesimal filename `fname` (a 36-bit register with six packed characters) and checks to see if a file with that name exists in the paravirtualized filesystem. If it does, the file is deleted. If the file does not exist, nothing happens and no error is indicated. `UNLINK` works on file names, not on open file handles. It does not matter whether the file is open or not. (Note that if you `UNLINK` an open file on a Linux host, the file doesn't go away until all programs have closed it.) The assembler does not enforce signedness for this opcode.