On this article I will disect the rom_i2c_readReg function. This function exhibits a really insteresting behaviour, suposedly it accesses the internal i2c which connects the peripherals not directly mapped to memory (PLLs, and whatnot).
Lets get started:
a couple of i2c related functions that live in the rom:
uint8_t rom_i2c_readReg(uint8_t block, uint8_t host_id, uint8_t reg_add);
void rom_i2c_writeReg(uint8_t block, uint8_t host_id, uint8_t reg_add, uint8_t data);
Before speculating over how the registers work, let's take a look into the disassembly
rom_i2c_readReg(103, 4, 1);
; uint8_t rom_i2c_readReg(uint8_t block => a2, uint8_t host_id => a3, uint8_t reg_add => a4) => a2;
40007268: f33b81 l32r a8, 0x40003f54 ; a8 = *(uint32_t*)0x40003f54 => 0x60000A00
4000726b: 116480 slli a6, a4, 8 ; a6 = reg_add << 8
4000726e: 1173e0 slli a7, a3, 2 ; a7 = host_id << 2
40007271: 206260 or a6, a2, a6 ; a6 = block | a6
40007274: 278a add.n a2, a7, a8 ; a2 = a7 + a8
40007276: 0020c0 memw ; memory wait (you can ignore this, see the ISA)
40007279: c06262 s32i a6, a2, 0x300 ; *(uint32_t*)(a2 + 0x300) = a6
4000727c: 0020c0 memw ;
4000727f: c02252 l32i a5, a2, 0x300 ; a5 = *(uint32_t*)(a2 + 0x300) => a5
40007282: 0a7597 bbci a5, 25, bit_clear ; if(!(a5 & (1 << 25))) goto bit_clear;
loop: ;
40007285: 0020c0 memw ; basically this block waits until bit25 is clear
40007288: c02272 l32i a7, a2, 0x300 ; a7 = *(uint32_t*)(a2 + 0x300) => register is re-read
4000728b: f03d nop.n ; no operation
4000728d: f4f797 bbsi a7, 25, bit_set ; if(a5 & (1 << 25)) goto loop;
bit_clear: ;
40007290: 0020c0 memw ;
40007293: c02222 l32i a2, a2, 0x300 ; a2 = *(uint32_t*)(a2 + 0x300)
40007296: 752020 extui a2, a2, 16, 8 ; a2 = (a2 >> 16) & 0xff
40007299: f00d ret.n ; returns a2
This basically writes, and reads the registers starting at 0x60000d00 adding some transformations to its arguments. I've observed that these registers are a two part 16bit register. The least significant 16 bits correspond to some kind of address, and writing to this part causes the data on the most significant 16 bits to change.
As seen on the code above, it's clear that there is a flag that is clear when the read is complete (bit 25 or 0x200 on the most significant 16 bits), and also note that reading some registers hangs the ESP8266.
In summary; the internal i2c registers are probably defined as:
struct {
uint8_t flags;
uint8_t data;
uint16_t current_address;
};