• Updated 2023-07-12: Hello, Guest! Welcome back, and be sure to check out this follow-up post about our outage a week or so ago.

Writing to a read-only (ROM) socket

Bunsen

Admin-Witchfinder-General
http://bitchin100.com/wiki/index.php?title=REX_Overview
 

REX is a low power 1MB flash card custom designed to work with the [Tandy] Model [100]. The challenge for REX is to be able to accomplish a "write" in a socket designed only for "reads".
/

The REX Communication Method

REX uses a unique approach to bi-directional communication in a read-only socket, which is 100% reliant on the architecture of the 8085.
Reads are simple - the socket is designed for Read Only.
Writes are the challenge, with the signals available.
 
The 8085 uses a multiplexed address and data bus, and the ALE signal is used to discriminate address from data early in the CPU cycle. Normally address data is captured on the falling edge of ALE. We use this fact to send data to REX embedded in the lower address byte.
 
REX watches the address bus for READ operations (signaled by /CS to the option rom socket), and listens to the lower byte.
 
To send data to REX then, one must perform a READ to the optrom socket, where the desired data is actually the lower address field. REX is clocked from ALE, so it will respond to stimulus from the bus on the falling edge of ALE.
 
REX mostly ignores this data, and acts like a normal option rom, until it senses the key sequence has been sent. When a sequence of 6 reads from addresses with specific bytes in the lower address field are seen, REX transitions from the default power up state into the command state.
/
REX intercepts the address/data bus AD0-AD7, and the system communicates with the CPLD using data sent on these lines.
Data is sent to REX on the falling edge of the ALE signal. During this time, the address/data bus AD0-AD7 contains the lower address information for that CPU cycle.
So, in order to send data to REX, the data is sent as the bottom 8 bits of the ADDRESS, not the DATA, during a read cycle from the option rom.
/
Now, granted, the 68k does not use a multiplexed address/data bus, but - does this stir any ideas?

 

Gorgonops

Moderator
Staff member
The classic "writable thing in a ROM socket" device is the Dallas Smartwatch. This device was known in the Apple II world as a "Zero Slot Clock", but it was also sold for use in PC compatibles and elsewhere. It looks like this REX thing uses an essentially similar technique. (The Dallas Smartwatch uses a particularly elaborate "key sequence" because it *really* goes out of its way to make sure it's not inadvertently triggered; you can be sloppier if the socket space you're using is otherwise unused.) The description of the technique from the REX people is a little confusing and I don't quite get the bit where it yaks about explicitly leveraging the 8085's multiplexed bus; the bus is de-multiplexed before it hits the ROM sockets and the method of encoding the data to be written as an address in the low bits is de rigueur regardless of the host CPU.

If dig through the archives here you'll find a thread about a homebrew SCSI card for the Mac 128/512 that predated the Mac Plus. (I think it was made by a guy named "John Bass"?) That device used the "write the data as an address" technique and the explanation was somewhat less opaque.

 

bigmessowires

Well-known member
GREAT topic. I love it. I've faced this exact question a few times.

The REX method makes good sense, if your ROM device contains some kind of mcu or other logic that can detect the magic address sequence needed to unlock it for writing. Perform a series of dummy reads in the proper sequence, then do one more read, which is actually treated as a write by the device. But if the magic sequence is too simple, or just boils down to reads in a particular address range being treated as writes, it will likely fail at some point. There are lots of situations that can cause phantom reads from unexpected addresses. It depends on the CPU architecture and the system design, but in general I think it's a poor practice to assume that if your code never puts XXXX on the address bus, then XXXX will never appear on the address bus. I treat the address bus as some kind of wild unknown that will conspire to be in exactly the wrong state to screw up my design. I've been bitten by this bug more than once.

Another drawback of the REX approach is that data inputs are connected to the low-order address bits, but data outputs are connected to the data bus. This requires some extra muxing or buffering logic in the device, which will increase its complexity and cost a little.

For the Mac ROM-inator board I designed (replaceable ROM for the Mac 128K, 512K, and Plus), I wanted to make the ROM reprogrammable from within the running system. So I faced the exact same challenge as REX did, and considered a similar solution. But the ROM-inator doesn't have an mcu or other logic that could be used to detect a magic unlock sequence. It already required soldering two new connections to the logic board or CPU for the upper address lines, so in this case I felt it was reasonable to require one additional soldered connection to the CPU's write-enable /WE output. If there hadn't already been other required soldered connections, I would probably have made a different choice, and either added a simple MCU to do the REX method, or just dropped the idea of in-system reprogramming.

 

Elfen

Well-known member
Like BMOW & Gorgonops stated, it depends on the chip and the support circuitry to the chip. You can tell the CPU to take a value and try to write it/store anywhere in memory, and it would return no error unless you try to verify it that you wrote to it and see nothing's changed.

On 6502:

LDA #$01 ;Load the accumulator register with the value #$01

STA $FFF0  ;Store accumulator register in this area of memory somewhere in ROM.

; No Error is produced, the CPU did as it was told, no matter how ridiculous it maybe.

; Now to verify...

LDA $FFF0  ; Load accumulator register with the contains of memory in $FFF0

CMP A, #$00  ;Compare the loaded accumulator register with the value #$00

BEQ Correct  ; If it is true, then jump to the routine/subroutine in Correct.

BNE Incorrect  ; If it not true, then jump to Incorrect routine/subroutine.

BRK  ;Stop CPU Program here.

With the Apple IIs, some of the ROMs are actually EPROMs, and if you put the correct write voltage to the Program Pin (27V in some cases), the Write Enable is activated and the memory in ROM Space is changed. Same thing on some Macs - the ROMs are EPROMs. I'm just wondering how many Macs out there use EEPROMs and not EPROMs. The support circuitry to write to an EEPROM is simpler to make than for a EPROM; depending on the chip.

The problem I see is in security - the use of FLASH EPROMs in portable systems like tablets and smartphones. One properly written virus, and damn! You just wiped out your system. On an old iPod Touch (Gen 3), I found it to have some nasties in it, and not necessarily for the iPod but for the system it connects to. OK, I bought it used from a pawn show, but hey - $25 for an iPod Touch ten years ago, it was a steal! So I had to do a Factory Reset, and take to Apple to program a new OS into it (did not have the broadband at the time to do it myself).

For every good idea out there, there is some idiot out there willing to exploit it for bad.

 

Gorgonops

Moderator
Staff member
One thing to be clear on about the "smartwatch" technique is it doesn't really work well for random-access memory devices. Remember, the technique depends on encoding the data you want to write into an address, you don't actually write with the data lines themselves. So, for instance, let's say you've hung some sort of disk controller in a ROM socket. Pretend that said disk controller uses four contiguous I/O ports selected with two address lines (A0 and A1) and an 8 bit data bus, and the ROM socket you put it in is mapped to unused space so you don't have to worry about needing a "key" sequence to inform the control circuitry that you want to access the disk controller instead of a ROM in the same space. Also in this case there's:

A: No pin on the socket you can use to determine if the CPU is requesting a read or a write, all there is is an "output enable" line, and:

B: There's a buffer on the data lines upstream of you that only opens when the CPU is indicating it wants to *read* the ROM; if you do a write the data pins just stay tri-stated. I don't think there's such a thing on the Mac but you might find it on other systems.

The second condition means you can't (trivially) run a flying wire to somewhere else on the board to get a write enable signal and make the socket bidirectional, the *only* signals you can get *from* the CPU are addresses and read requests. So, what you do is this:

Attach *both* A0 through A7 of the address bus and D0 through D7 of the data bus to a multiplexer that sits in front of the data. Then attach A8 and A9 of the address bus to A0 and A1 (the I/O port selects) on the controller chip and A10 to a collection of gates set up to controls whether which "port" on the multiplexer is open (to the address bus or the data bus) and the R/W signal on the controller based on the status of that line and the OE line in the socket. Now your disk controller, which in a normal memory-mapped configuration would occupy 4 bytes of address space now occupies a whopping 2k but can be controlled completely by *reading* the memory socket.

In this case you choose to set it up so the first 1k of space is for reading the chip while the second 1k is for writing data to it. To read a byte from any of the four ports your psuedocode would look like this:

BASEADDR=(Wherever in memory your socket starts; needs to be on a 2k threshold.)

PORTADDR=256 * X (X= 0 through 3, depending on which port you want)

DATAADDR=0 (this actually doesn't matter)

TRANSADDER=BASEADDR+PORTADDR+DATAADDR

BYTEREAD = (readmem) TRANSADDAR

Each of the four ports on the peripheral are 256 bytes apart, which is why the multiply is in there (simply shifting the value would actually make more sense but multiplication makes it more straightforward to explain.) Port thus selected since this is a read the multiplexer on your peripheral shunts the data output from the lines on the controller chip directly the data bus and there you go. So what if you want to write again?

BASEADDR=1024+(Wherever in memory your socket starts; needs to be on a 2k threshold.)

PORTADDR=256 * X (X= 0 through 3, depending on which port you want)

DATAADDR=(Byte you want to write)

TRANSADDER=BASEADDR+PORTADDR+DATAADDR

DUMMYREAD = (readmem) TRANSADDAR

I'll leave it to the reader to figure out why this works. You can see how this wouldn't really work well for a random-access memory. It's *possible* to do it but you need some buffering and multiple transactions to write to an arbitrary byte location.

 
Last edited by a moderator:

johnklos

Well-known member
That's not a bad idea if you want to make something that plugs between a ROM and a motherboard, but you'd have to have two for older Macs.

Another design which may be more relevant to Macs is to have RAM and ROM mapped in the same memory location, but with chip select dependent on read versus write. Then, after the system is booted, a program could leisurely read a location from ROM, then write it to the same location to go in to RAM. When that's finished, one could then insert patches where one wants, then switch the chip select for reads from the ROM to the RAM.

This makes for an easy way to bootstrap new hardware, too.

 

Elfen

Well-known member
Like the Commodore 64 and 128? On a Mac? Hmmmm... I think I stated something along those lines a couple years ago and it was "stopped" because of the Eagle Chip getting in the way on some of the designs. On emulation like on vMac, this would be a great proof of concept to test out. The question is - how?

And if proven to be right - it would be fun to do on FPGA. Hair pulling, system cursing, keyboard fist banging frustration fun!

 
Top