// Commander X16 Emulator — names and purposes for the I/O page registers.
2
//
3
// See io_reg_info.h. Everything here is transcribed from the emulator's own
4
// decode: VERA from video_read/video_write in video.c, the VIA registers and
5
// pin assignments from via.c, the emulator registers from emu_read/emu_write
6
// in memory.c, and the SMC/RTC maps from smc.c and rtc.c.
7
8
#include"io_reg_info.h"
9
10
#include<stddef.h>
11
12
namespace{
13
14
structRegInfo{
15
constchar*name;
16
constchar*purpose;
17
};
18
19
constchar*const UNKNOWN_NAME ="-";
20
constchar*const UNKNOWN_PURPOSE ="Not decoded by this emulator. Reads return the open-bus value $9F.";
21
22
// --- VIA, $9F00-$9F0F (VIA1) and $9F10-$9F1F (VIA2) ------------------------
23
// Standard 65C22 layout; both VIAs use it.
24
const RegInfo VIA_REGS[16]={
25
{"ORB/IRB","Port B data. Reading clears the CB1/CB2 interrupt flags unless ACR latching is on."},
26
{"ORA/IRA","Port A data. Reading clears the CA1/CA2 interrupt flags unless ACR latching is on."},
27
{"DDRB","Port B direction. A 1 bit makes that pin an output, a 0 bit an input."},
28
{"DDRA","Port A direction. A 1 bit makes that pin an output, a 0 bit an input."},
29
{"T1C_L","Timer 1 counter low. Reading returns the live counter and clears the T1 interrupt flag; writing loads the latch low byte without starting the timer."},
30
{"T1C_H","Timer 1 counter high. Writing loads the latch high byte, clears the T1 flag, copies the latch into the counter and STARTS the timer."},
31
{"T1L_L","Timer 1 latch low. Reaches the latch directly, without touching the running counter or the interrupt flag."},
32
{"T1L_H","Timer 1 latch high. Writing loads the latch and clears the T1 flag but does NOT restart the counter -- that is what T1C_H is for."},
33
{"T2C_L","Timer 2 counter low. Reading returns the live counter and clears the T2 interrupt flag; writing loads the latch low byte."},
34
{"T2C_H","Timer 2 counter high. Writing loads the latch, clears the T2 flag and starts the timer. T2 is one-shot only."},
35
{"SR","Shift register. Mode comes from ACR bits 4-2. Note: shift register operation is not fully implemented in this emulator."},
36
{"ACR","Auxiliary control. b7 T1 drives PB7, b6 T1 free-run vs one-shot, b5 T2 counts PB6 pulses vs time, b4-2 shift mode, b1/b0 port latching."},
{"ADDRx_L","VRAM address bits 7-0 for whichever data port ADDRSEL selects."},
46
{"ADDRx_M","VRAM address bits 15-8 for the selected data port."},
47
{"ADDRx_H","VRAM address bit 16 (b0) plus the auto-increment step in bits 7-3, and the FX nibble controls in b2/b1."},
48
{"DATA0","Auto-incrementing VRAM data port 0. Every access moves the address on, so this is the busiest register in the machine -- and why VERA capture is off by default."},
49
{"DATA1","Auto-incrementing VRAM data port 1. Same as DATA0 but uses the second address register."},
50
{"CTRL","b0 ADDRSEL picks which address port ADDRx_* refers to; b6-1 DCSEL banks the four registers at $9F29-$9F2C; b7 soft-resets VERA."},
51
{"IEN","Interrupt enable: b0 VSYNC, b1 LINE, b2 sprite collision, b3 PCM FIFO almost empty. b7 carries bit 8 of the raster compare line."},
52
{"ISR","Interrupt status, same bit order as IEN. Write a 1 to a bit to acknowledge and clear it."},
53
{"IRQLINE_L","Bits 7-0 of the scanline that raises a LINE interrupt. Bit 8 lives in IEN b7."},
54
{"DC_VIDEO*","DCSEL-banked. DCSEL=0 selects video output mode and chroma; other DCSEL values make this the display window start, or an FX register. Which one applies depends on CTRL at the time."},
55
{"DC_HSCALE*","DCSEL-banked. DCSEL=0 is the horizontal scale factor (128 = 1:1); other DCSEL values select display-window or FX registers."},
56
{"DC_VSCALE*","DCSEL-banked. DCSEL=0 is the vertical scale factor (128 = 1:1); other DCSEL values select display-window or FX registers."},
57
{"DC_BORDER*","DCSEL-banked. DCSEL=0 is the border colour index; other DCSEL values select display-window or FX registers."},
{"AUDIO_CTRL","PCM audio: b3-0 volume, b4 stereo, b5 16-bit samples. Reading gives FIFO full in b7 and FIFO empty in b6; writing b7 resets the FIFO."},
73
{"AUDIO_RATE","PCM playback rate. Zero stops playback; otherwise the rate is the value times 25MHz/65536."},
{"SPI_DATA","SPI data to and from the SD card. This is the ONLY path to the card, which is why it is traced as its own device rather than as part of VERA."},
76
{"SPI_CTRL","SPI control. Read: b7 busy, b2 auto-transmit, b0 slave select. Write: b0 selects or deselects the card, b2 enables auto-transmit."},
{"DBG_EN","Reads 1 when the text debugger is enabled. Writing non-zero enables it."},
82
{"LOG_VIDEO","Reads 1 when VRAM access logging is on. Writing non-zero logs every VRAM read and write to stdout."},
83
{"LOG_KBD","Reads 1 when keyboard logging is on."},
84
{"ECHO","Echo mode: controls how KERNAL character output is mirrored to the host terminal."},
85
{"SAVE_EXIT","Reads 1 when the emulator will save state on exit."},
86
{"GIF_REC","GIF recorder state. Writing sends a start/stop command."},
87
{"WAV_REC","WAV recorder state. Writing sends a start/stop command."},
88
{"CMD_KEYS","Reads 1 when the emulator's own hotkeys are disabled."},
89
{"CLK_SNAP_L","Reading LATCHES the 32-bit cycle counter and returns bits 7-0. Read this first, then the other three, to get a coherent value. Writing resets the count."},
90
{"CLK_SNAP_ML","Bits 15-8 of the cycle count latched by reading CLK_SNAP_L. Writing prints a user debug byte to stdout."},
91
{"CLK_SNAP_MH","Bits 23-16 of the latched cycle count. Writing prints a second user debug byte to stdout."},
92
{"CLK_SNAP_H","Bits 31-24 of the latched cycle count. Writing sends a character to the host's stdout."},
93
{"-","Not implemented; warns on access."},
94
{"KEYMAP","The current keyboard layout index."},
95
{"DETECT_1","Reads ASCII '1'. With the next register it spells \"16\", which is how a program detects it is running under the emulator."},
96
{"DETECT_6","Reads ASCII '6'. See DETECT_1."},
97
};
98
99
constchar*const VIA1_PA[8]={
100
"I2CDATA -- I2C data line (SDA) to the SMC and RTC",
101
"I2CCLK -- I2C clock line (SCL)",
102
"NESLATCH -- asserted to latch the controllers' button state",
103
"NESCLK -- shifts the next button bit out of every controller",
104
"NESDAT3 -- serial data in from controller slot 3",
105
"NESDAT2 -- serial data in from controller slot 2",
106
"NESDAT1 -- serial data in from controller slot 1",
107
"NESDAT0 -- serial data in from controller slot 0",
108
};
109
110
constchar*const VIA1_PB[8]={
111
"unused",
112
"unused",
113
"unused",
114
"IECATN_OUT -- IEC serial attention out",
115
"IECCLK_OUT -- IEC serial clock out",
116
"IECDAT_OUT -- IEC serial data out",
117
"IECCLK_IN -- IEC serial clock in",
118
"IECDAT_IN -- IEC serial data in; also the T1 square-wave output when ACR b7 is set",
119
};
120
121
constchar*const VIA_IRQ_BITS[8]={
122
"CA2 -- not wired on the X16",
123
"CA1 -- not wired on the X16",
124
"SR -- shift register finished eight shifts",
125
"CB2 -- not wired on the X16",
126
"CB1 -- IEC service request",
127
"T2 -- timer 2 underflowed",
128
"T1 -- timer 1 underflowed",
129
"IRQ -- read-only: set when any enabled flag above is pending",
130
};
131
132
structSmcReg{
133
uint8_t reg;
134
constchar*name;
135
constchar*purpose;
136
};
137
138
const SmcReg SMC_TABLE[]={
139
{0x01,"PWR_CTRL","Write 0 to power the machine off, 1 to hard reboot it."},
140
{0x02,"RESET_BTN","Write 0 to act as though the reset button was pressed."},
141
{0x03,"NMI_BTN","Write 0 to act as though the NMI button was pressed."},
142
{0x04,"PWR_LED","Power LED brightness. Accepted but ignored by this emulator."},
143
{0x05,"ACT_LED","Activity LED. The emulator treats it as on/off at a threshold of 128, not as a brightness."},
144
{0x07,"KBD_BUF","Reading takes the next byte from the 16-byte keyboard ring buffer, or 0 when it is empty."},
145
{0x09,"PWR_LONG_PRESS","Reads 1 if the machine was started by a long power-button press. Reading clears it."},
146
{0x20,"MOUSE_DEV","Sets the mouse device id: 0 for none, 3 for a standard mouse. Flushes the mouse buffer."},
147
{0x21,"MSE_BUF","Reading takes the next byte of a mouse movement packet, or 0 when no complete packet is waiting."},
148
{0x22,"MOUSE_DEV_ID","Reads back the current mouse device id."},
{0x40,"DEFAULT_READ_OP","Sets which register a bare I2C read -- one with no register select in front of it -- will return."},
153
{0x41,"KBD_BUF_DEF","Reads one keyboard byte, the same as $07. This is the power-on default for bare reads."},
154
{0x42,"MSE_ONLY_BUF","Reads mouse bytes only."},
155
{0x43,"KBD_MSE_COMBO","Reads a keyboard byte, then cycles through the mouse packet on subsequent reads."},
156
};
157
158
const SmcReg *
159
find_smc(uint8_treg)
160
{
161
for(size_t i =0; i <sizeof(SMC_TABLE)/sizeof(SMC_TABLE[0]); i++){
162
if(SMC_TABLE[i].reg== reg){
163
return&SMC_TABLE[i];
164
}
165
}
166
returnNULL;
167
}
168
169
}// namespace
170
171
constchar*
172
io_reg_name(uint16_taddr)
173
{
174
if(addr >=0x9f00&& addr <0x9f10){
175
return VIA_REGS[addr &0x0f].name;
176
}
177
if(addr >=0x9f10&& addr <0x9f20){
178
return VIA_REGS[addr &0x0f].name;
179
}
180
if(addr >=0x9f20&& addr <0x9f40){
181
return VERA_REGS[addr &0x1f].name;
182
}
183
if(addr >=0x9f40&& addr <0x9f60){
184
return(addr &1)?"YM_DATA":"YM_ADDR";
185
}
186
if(addr >=0x9fb0&& addr <0x9fc0){
187
return EMU_REGS[addr &0x0f].name;
188
}
189
return UNKNOWN_NAME;
190
}
191
192
constchar*
193
io_reg_purpose(uint16_taddr)
194
{
195
if(addr >=0x9f00&& addr <0x9f10){
196
return VIA_REGS[addr &0x0f].purpose;
197
}
198
if(addr >=0x9f10&& addr <0x9f20){
199
return VIA_REGS[addr &0x0f].purpose;
200
}
201
if(addr >=0x9f20&& addr <0x9f40){
202
return VERA_REGS[addr &0x1f].purpose;
203
}
204
if(addr >=0x9f40&& addr <0x9f60){
205
return(addr &1)
206
?"YM2151 data port and status. Writing sends a byte to the register YM_ADDR named; reading returns the busy and timer status. Partially decoded across $9F40-$9F5F, so every odd address in that range is this register."
207
:"YM2151 register select. Write the register number here, then the value to YM_DATA. Partially decoded, so every even address in $9F40-$9F5F is this register.";
208
}
209
if(addr >=0x9fb0&& addr <0x9fc0){
210
return EMU_REGS[addr &0x0f].purpose;
211
}
212
return UNKNOWN_PURPOSE;
213
}
214
215
constchar*
216
io_via_pin_name(intwhich,charport,intbit)
217
{
218
if(bit <0|| bit >7){
219
return"?";
220
}
221
if(which !=0){
222
// VIA2 is the user port, and this emulator wires it to nothing.
223
return"user port -- not connected in the emulator";
224
}
225
return(port =='A')? VIA1_PA[bit]: VIA1_PB[bit];
226
}
227
228
constchar*
229
io_via_irq_bit_name(intbit)
230
{
231
return(bit >=0&& bit <=7)? VIA_IRQ_BITS[bit]:"?";
232
}
233
234
constchar*
235
io_smc_reg_name(uint8_treg)
236
{
237
const SmcReg *r =find_smc(reg);
238
return(r !=NULL)? r->name:"-";
239
}
240
241
constchar*
242
io_smc_reg_purpose(uint8_treg)
243
{
244
const SmcReg *r =find_smc(reg);
245
return(r !=NULL)? r->purpose:"Not a register this emulator's SMC recognises; reads return $FF.";
246
}
247
248
constchar*
249
io_rtc_reg_name(uint8_treg)
250
{
251
switch(reg){
252
case0x00:return"RTCSC";
253
case0x01:return"RTCMN";
254
case0x02:return"RTCHR";
255
case0x03:return"RTCDOW";
256
case0x04:return"RTCDATE";
257
case0x05:return"RTCMTH";
258
case0x06:return"RTCYR";
259
default:break;
260
}
261
if(reg >=0x20&& reg <0x60){
262
return"NVRAM";
263
}
264
return"-";
265
}
266
267
// Mirrors the dispatch in command() in src/ieee.c. Two-letter forms have to be
268
// tested before their one-letter prefixes, exactly as the switch there does.