jlxip/RealDRAM
An architecture with non-refreshed DRAM
RealDRAM
An architecture with non-refreshed DRAM.
What?
RealDRAM is a really simple esoteric architecture in which memory is lost after a few cycles (default is 128), so it needs to be refreshed manually. This repository contains a virtual machine for it, made in C++. Install it with sudo make install, and use it like rdram-vm.
There is a debug mode to analyze binary executions. Uncomment the second line of vm.cpp before compiling to use it. It's disabled by default to boost performance.
RealDRAM has 8-bit words and 16-bit addresses (64K of memory). It should be Turing-complete (not proven), even though it has only "two" instructions. These instructions are BRANCH (conditional branch) and NAND. The RealDRAM VM executes a raw binary, loaded at 0x0000, which is the entry point. The first byte of an instruction is the opcode, and the rest are one or two Little-Endian addresses.
The architecture has no usable registers, but there is a flag which is updated on NAND operations.
Opcodes ๐ฅ๏ธ
The first bit of the opcode (starting from the left) indicates whether it's a BRANCH (0) or NAND (1).
BRANCH ๐ธ
The branch instruction is a conditional branch, which will only fire if the flag is up.
The second bit of the opcode says whether the branch is direct (0) or indirect (1). The next two bytes have the address.
In direct branches, the jump is quite straightforward. Indirect branches will read two bytes starting from that address and then jump to the address stored there. Remember: Little-Endian.
NAND ๐ฃ
NAND opcodes have 7 extra bits of information:
- Bit (
0) or byte (1). Whether this NAND affects a single bit or the whole byte pointed by the address. - Other (
0) or itself (1). If this NAND's origin is the same as the destination. That is, NANDing to itself. - HI, MID, and LO. Three bits used in bit NANDs, they indicate the bit to touch, so
010= third bit STARTING FROM THE RIGHT. In byte NANDs they are not checked, so (hint) they can be used to store whatever you want. - Orig D/I and Dest D/I. If the origin and/or the destination are direct addresses (
0) or indirect (1), much like with the branch.
The next two bytes contain the address of the origin, and if the NAND is not to itself, the following two have the destination.
NAND is the only instruction that updates the flag, which is set to the result of the operation in bit NANDs. In byte NANDs, it's the OR of all the resulting bits.
For example, 101010000000000000000000. Its bytes would be 10101000 11111111 00000000, the opcode indicates NAND-BIT-ITSELF-2, and the address is 0x00FF. So, if that address contains 0x00, the result would be 0x04. The flag would be 1, as NAND(0, 0)=1.
Memory loss ๐ง
Byte NANDs cost 8 cycles. Any other instruction costs 1 cycle.
When a byte is written for the first time, it starts rotting. Bytes that are not touched in runtime (such as the binary when loaded) do not suffer from the loss, neither read bytes do, so the origin of a NAND-OTHER does not necessarily rot.
Even if only a bit has been changed, the whole byte starts to rot. However, it's only necessary to change one bit (any) to refresh a whole byte.
To refresh a byte, an option is to NAND any of its bits twice.
Memory mapped I/O ๐ฎ
The VM supports simple I/O.
Byte at 0xFFFF is a buffer for console interaction. It will rot when written by the program. It will not rot when written by the VM. Writing to 0xFFFD will trigger a read from stdin and put the byte in the buffer. Writing to 0xFFFE will trigger a write to stdout.
Bytes at 0xFFFD and 0xFFFE are always zero, even after you write to them, so they can be used as a fixed address to get a zero byte. If the binary is large enough to be 64KB long, they will not be zero, but whatever the binary specifies, but they still won't be changed.
Jumping to 0xFFFF will terminate the program.
Why?
See this reddit thread.
Extra
An example program, echo, can be found in the examples directory. Compile it with asm.py echo, which will generate echo.bin.
What's next?
There may be bugs! This was programmed really fast, so probably there's a bug hidden somewhere. If you find one, please open a pull request :)
I propose the following ideas to the community, in case someone wants to waste their time with this:
- Prove the architecture is Turing-complete.
- A program that adds two numbers from input.
- An assembler for RealDRAM. DONE, see below.
- A high level language. Creating a compiler that makes sure no necessary memory is lost would be quite challenging.
If you do one of these projects or a cool program for RealDRAM, open an issue and I will link your repo here :D
Related projects
- I've written an assembler for this architecture, RDRAMA.