Experimentations with 33554432 and Meta-CHIP-8 The initial design and release of my MMC targeted at CHIP-8 are intertwined with my experimentations regarding a pair of my machine code designs, both memory-to-memory, and both ultimately unnecessary. The prior version of this article contained many subjunctive details and whatnot which I've removed. The vague machine code 33554432 inspired me to create the MMC; it's a set of twenty-five bits acting as switches, hence the name, and I recognized the queer design was obviously unsuited to assemblers. My expectation, back in 2015, was this design would perhaps help facilitate self-modifying programs; that first bit was a ``nothing'' bit which had such instructions ignored, enabling a loop to disable its initialization code. My basic idea was that each bit would signal an operation, and these would be specially chosen to compose well; I regard this machine code as a failure with a pleasant result. My 2017-05-07 article contains further thoughts regarding what I seek out with some machine designs. My Meta-CHIP-8 underwent two revisions: one only described, and that which was actually implemented. The purpose of Meta-CHIP-8 was to manipulate CHIP-8 programs in the context of my Meta-Machine Code. So, I designed Meta-CHIP-8 to resemble CHIP-8 in form and function; I also wanted to experiment with memory-to-memory machines, which led to some fine revelations for the model, yet failed in producing an overall nice design. I never designed any tool for writing Meta-CHIP-8, programming it manually. The basic form of Meta-CHIP-8 was a uniform twenty-four bit or three octet instruction format, split into octets or nibbles. The first nibble was the command code; the following octet two-thirds of an address, combined with the program counter to form a total address; and the latter half being either a total address, or a second nibble as a subcommand code followed by a second octet partial-address. This design naturally split memory into sixteen octet blocks of two hundred and fifty-six each. The machine wasn't unpleasant to program, and the blocks were reasonably large for programs and storage. The program counter and various hooks into the MMC and whatnot were implemented by providing special addresses in that final block of memory. One idea of mine was having the penultimate pair of octets hold the previous program address, and attempt to replace explicit subroutine support with a general mechanism, but this didn't have the intended result, merely being bothersome and shifting the burden to the called, in place of the caller. Follows is the relevant memory layout of the ultimate block: FE0 FE1 FE2 FE3 FE4 FE5 FE6 FE7 quit jump up down save instate insert delete FF0 FF2 FF3 FF4 FF5 FF6 FF7 FFA FFB FFC FFD FFE FFF escape calling address random deposit size calling hextet penultimate PC ultimate PC program counter The FEX range was used for program termination by calling a system routine, with FE0 and FE1 serving as argument spaces, FE2 and FE3 then serving as additional argument spaces, where necessary. System functionality, such as quitting, would be gated by a routine asking for confirmation before jumping. Follows is a description of the special instructions, using a lone command code, an octet, and total address. These vary with how the address and octet are used, and so are akin to specialized axioms: L - Local address formed by octet; A - Total address; P - Program space total address; I - implicit destination, the final local octet 0 L <- A; Transfer the octet of the total address to the local address. 1 A <- L; Transfer the octet of the local address to the total address. 2 L <- A; L+1 <- A+1; Transfer the hextet of the total address to the local address. 3 A <- L; A+1 <- L+1; Transfer the hextet of the local address to the total address. 4 L <- P; Transfer the octet of the program space to the local address. 5 P <- L; Transfer the octet of the local address to the program space. 6 L <- P; L+1 <- P+1; Transfer the hextet of the program space to the local address. 7 P <- L; P+1 <- L+1; Transfer the hextet of the local address to the program space. 8 Disassemble the hextet beginning from L as a CHIP-8 instruction. An XYYY begets a one followed by that command code and hextet containing the address; an XYZZ begets a two followed by that command code, an octet containing Y, and the ZZ octet; and an WXYZ begets a three followed by that command code, an octet containing X, an octet containing Y, and an octet containing Z; the output is to A. 9 Assemble the output of disassemble beginning from A, as a CHIP-8 instruction, beginning from L; if the first octet is zero, it represents Meta-CHIP-8 instructions by two nibbles, octet, and hextet. A Interpret the octet as a count and show a string of count characters beginning from A. B Interpret the octet as a limit and read in characters below, placing them from A; I is the length. C Interpret the hextet beginning at L as a limit; read an integer, no larger, to deposit as octet or hextet from A, determined by the limit permitting this or not. Place the name used at I, or zero, if no name was used; D is C starting in name entry. If defaults are given, memory is undisturbed. E This is the extended command code. F Stop Meta-CHIP-8 processing. I'm particularly pleased with the design of 8, 9, C, and D. Designing a machine code so that it may easily self-modify is crucial, in my eyes, and Meta-CHIP-8 used this to achieve indirect addressing. The decision to have C and D leave memory undisturbed when the defaults are used automatically gives a mechanism for providing default values, and almost stops the system from knowing if this was used. The F entirely ignores its arguments, doing little, and the space can be treated as any other manner of memory. Follows is a description of the extended command codes; these are, mostly, more uniform: 1 - first local address; 2 - second local address 0 Sum the octets of 1 and 2, to 2; I is set to zero or one to indicate overflow. 1 Delta the octets of 1 and 2, to 2; I is set to zero or one to indicate underflow. 2 Multiply the octet of 1 by two, to 2; I is set to the prior MSB of the octet of 1. 3 Divide the octet of 1 by two, to 2; I is set to the prior LSB of the octet of 1. 4 Skip the following instruction if the octets of 1 and 2 are equal. 5 Skip the following instruction if the octets of 1 and 2 aren't equal. 6 Place the three-octet decimal representation of the octet of 1 beginning from 2. 7 Place the five-octet decimal representation of the hextet of 1 beginning from 2. 8 Logically AND the octets of 1 and 2, to 2. 9 Logically IOR the octets of 1 and 2, to 2. A Logically XOR the octets of 1 and 2, to 2. The B encompasses a variety of miscellaneous name manipulations, predicated by the high nibble of 1; 2 is a twenty-octet data: hextet name code; hextet value; octet label, size; and fifteen characters: 0 Populate the name data at 2 according to its name code. 1 Delete the name according to the name code. 2 Change the name by its identifier and update the name code field. 3 Create the name by its identifier and update the name code field. 4 Populate the name data at 2 according to the name identifier. 5 Populate the name data at 2 according to any label corresponding to the value. Those ultimate four instructions also interact with the name system, and treat each local address as also designating a hextet which holds a three-bit assocation and twelve-bit name code to manipulate: N - first name hextet; M - second name hextet C Fill the hextet of 2 with N. D Fill the name tables corresponding to the address of the hextet of 1 with M. E Have the hextet of 2 be the routine information for the address of the hextet of 1. F Have the routine entry for the address of the hextet of 1 be the address of the hextet of 2. This bizarre design was clearly harmed by its mimicking of CHIP-8; a prime advantage of some memory- to-memory designs, using memory-movement instructions for multiple purposes, was lessened by needing eight variations; using octets as the fundamental unit also harmed this. It was a useful experience for me, however; the design using total and local addresses is an interesting compromise to decrease instruction size, and many advantages were still present; memory management was generally simple, by treating the end of each block as the dumping ground for intermediate results. My plan for ultimate program counters to remove the need for routine mechanisms was misguided, as it requires any routine to know where it's called anyway, in order to save it for later. It was a mere valuable experiment. I may later dedicate an article to documenting the large program I wrote in Meta-CHIP-8, on my whim.