The following warnings occurred:
Warning [2] count(): Parameter must be an array or an object that implements Countable - Line: 795 - File: showthread.php PHP 7.4.33 (FreeBSD)
File Line Function
/showthread.php 795 errorHandler->error





Post Reply 
Observation about the HP-15C roll up and roll down microcode
02-21-2024, 09:35 AM
Post: #1
Observation about the HP-15C roll up and roll down microcode
In the course of getting the HP-15C working in the in-development Nonpareil II, I encountered a problem with the 15C roll down and roll up functions accessing non-existent data memory. Initially I assumed that I was simulating some instruction incorrectly. The TL;DR is that the instruction simulation is OK, but the memory simulation throws an exception for a read of nonexistent data memory, and such an access actually can happen on the real 15C (which of course doesn't throw exceptions).

The real 15C hardware will allow writes to non-existent memory locations, which are ignored, and reads from such locations, which return an all-zero word (due to the Nut CPU precharging the data line to zero for each bit of a read).

In Nonpareil II, I prefer to know when a non-existed data memory location is accessed, so the memory system I've implemented tracks what memory ranges are actually implemented, and causes an exception on accesses to unimplmeneted addresses. This is useful because usually on calculators with fixed memory maps, such accesses do not occur, therefore if they occur in simulation, it may be an indication of a simulation error, such as an instruction implemented incorrectly.

In calculators with expandable memory, or an otherwise variable address space, there are accesses to nonexistent locations. For instance, the 41C family probes the code address space for the diagnostic module, the printer module, and plug-in modules, which may or not be present, and it probes the data space for the existence of memory modules (41C and 41CV only) and extended memory modules. The 71B does similar things. The 28C (but not 28S) support addition of configurable RAM. The 48 etc. do various schenanigans with their memoy maps and bank switching. For those calculators, I'll ultimately have to tell the memory system to return zero, and not throw an exception, when an unimplmented address is read, and ignore writes.

Anyhow, getting back to the 15C. The R2D2 chips provide "status registers" at physical RAM addresses 0x00..0x0a and 0x10..0x1a, which include the user R00, R01, and I. They provide "user memory" at 0xc0..0xff. By default, if not in complex mode, and the SOLVE and INTEGRATE functions are not active, user register R02 is at 0xc0. If solve or integrate is active, a buffer for those starts at 0xc0, and the user registers start above that.Note that the SOVLE/INTEGRATE buffer, if it exists, is ALWAYS at the fixed address of the start of user RAM, 0xc0 on a normal 15C.

If complex mode is enabled, the imaginary parts of X, Y, Z, T, and L are allocated below R02, but above the SOLVE/INTEGRATE buffer if it exists.

The roll down microcode subroutine, which is called by roll down function, and also called three times by the roll up function, ALWAYS reads the imaginary part of X, even if it doesn''t exist. (It doesn't exist if the complex stack isn't allocated; this isn't a joke about imaginary numbers not being real.)

So of course to do that, they add an offset of -1 to the address of R02. If the complex stack is not allocated, this will access the last register of the SOLVE/INTEGRATE BUFFER, but if _that_ also isn't allocated, then it reads from address 0xbf, which is nonexistent. (Lower addresses with expanded-memory 15C firmware.)

After they read both real X and (possibly nonexistent) imaginary X, they call a subroutine that does a stack drop, and that doesn't read or write the imaginary stack if it's not allocated. When that returns, they check whether in complex mode, and only if so write the old imaginary X to imaginary T. Finally they write the old real X to real T.

Notice that if not in complex mode, they read the nonexistent imaginary X, but then never use the value, so on the real hardware it totally doesn't matter if they read either the last register of the SOLVE/INTEGRATE buffer, or a physically nonexistent location.

I didn't notice this when I wrote the original Nonpareil, as I implemented the Nut simulation initially for the 41C series, in which it is necessary for non-existent data memory to read as zero.

In conclusion, I didn't have to spelunk through a trace nearly as much as I expected, in order to find something going off the rails. There may still be bugs in my instruction simulation code, but if so, they weren't causing the access to nonexistent memory. The fix is that I now have the simulation allocating eight registers that are read-zero-write-ignore at 0xb8. (The data memory map is managed in multiples of eight registers.)

The Nut processor, if there's a microcode subroutine call to nonexistent code space, will read an all-zero instruction word. That would normally be a no-op instruction but as the first instruction after a subroutine call, it is instead treated as a return.

On the Saturn, they have a similar mechanism, but they specifically defined the opcode 00 (two consecutive zero nibbles) as the RTNSXM instruction, which always executes a return, but also sets the processor's nonexistent memory flag in the hardware status register. That way they don't have to special-case a NOP instruction. In fact, the only NOP instructions on the Saturn are actually GOTO instructions; there is no explicit NOP.
Find all posts by this user
Quote this message in a reply
Post Reply 


Messages In This Thread
Observation about the HP-15C roll up and roll down microcode - brouhaha - 02-21-2024 09:35 AM



User(s) browsing this thread: 1 Guest(s)