I use a microcontroller to access RAM and ROM a single clock cycle at a time on my ROM Emulator project and I noticed a problem accessing the VIA chip that way. I issue clock cycles that start and end in the high state to keep non-WDC CPUs static, which also leaves the CPU address and r/w lines active until the next clock cycle comes along. During memory read/write clock cycles the chip enable line goes low and high within the clock cycle which works fine for RAM and ROM but a read/write VIA clock cycle doesn't complete until I send another clock cycle of some sort.
So, the VIA clock cycle takes the clock lo. The CPU sets the address and r/w lines. The memory enable (RAM, ROM, or I/O) line goes low. The clock line goes high. The CPU puts data on the bus for a write cycle or reads the bus for a read cycle. The memory enable line (RAM, ROM, or I/O) goes high. The clock line remains high and the CPU address and r/w lines are still active.
Does the 65C22 VIA complete read/write transactions when the clock goes low rather than when the chip enable line is disabled?
TIA. Cheerful regards.
VIA anomaly ~ single-clock single-step (blind interface)
VIA anomaly ~ single-clock single-step (blind interface)
Last edited by Michael on Wed Feb 25, 2026 9:40 am, edited 3 times in total.
Re: VIA anomaly ~ single-clock single-step (blind interface)
Michael wrote:
During memory read/write clock cycles the chip enable line goes low and high within the clock cycle which works fine for RAM and ROM but a read/write VIA clock cycle doesn't complete until I send another clock cycle of some sort.
-- Jeff
Last edited by Dr Jefyll on Wed Feb 18, 2026 9:09 pm, edited 1 time in total.
In 1988 my 65C02 got six new registers and 44 new full-speed instructions!
https://laughtonelectronics.com/Arcana/ ... mmary.html
https://laughtonelectronics.com/Arcana/ ... mmary.html
Re: VIA anomaly ~ single-clock single-step (blind interface)
You should review the timing diagrams on page 46 of WDC's w65c22 datasheet (the values for the timings are in the preceding pages). The address, chip select, and R/W* should be set BEFORE the rising edge of PHI2 and the data will be captured on the falling edge of PHI2. I think your NOP is just creating the falling edge the 65C22 is waiting for.
I think what you are doing is not a standard bus cycle the 65C22 understands, so the expected behavior is undefined. When you have PHI2 high, you are in the middle of a cycle, not between cycles.
I think what you are doing is not a standard bus cycle the 65C22 understands, so the expected behavior is undefined. When you have PHI2 high, you are in the middle of a cycle, not between cycles.
- BigDumbDinosaur
- Posts: 9425
- Joined: 28 May 2009
- Location: Midwestern USA (JB Pritzker’s dystopia)
- Contact:
Re: VIA anomaly ~ single-clock single-step (blind interface)
Dr Jefyll wrote:
If you mean the VIA's chip enable line goes low and high within the time Phi2 is high, then that's a problem. The address decoder which drives a VIA's CS mustn't be qualified by Phi2. (Edit: hmm, your diagrams show it's NOT qualified by Phi2.
x86? We ain't got no x86. We don't NEED no stinking x86!
Re: VIA anomaly ~ single-clock single-step (blind interface)
Thank you for helping me think through this, guys. Turns out the VIA chip enable is available past the end of a 'pull' clock sequence when ϕ2 remains high and the address lines remain active because the I/O chip enable comes from the address decoder and not from the low-level 'pull' function. So I just need to generate a single-cycle 'nop' at the end of a series of I/O operations. My test ROM Emulator LCD driver code (below) contains a strategically placed 'nop' instruction and now I can write to the LCD display from the uC in ROM Emulator mode as well as from the 6502 in normal 6502 RUN mode.
Cheerful regards...
Cheerful regards...
Code: Select all
#define line1 (0x80+0x00) // set DDRAM command + line 1 address
#define line2 (0x80+0x40) // set DDRAM command + line 2 address
#define line3 (0x80+0x14) // set DDRAM command + line 3 address
#define line4 (0x80+0x54) // set DDRAM command + line 4 address
LCD lcd; // create 'lcd' instance of 'LCD' class
lcd.init(); // 8-bit mode, cursor inc & off
_delay_ms(1000); // wait 1-second
lcd.print("3P8B Backpack v1"); // splash screen
lcd.cmd(line2+2); // line 2, tab 2
lcd.print(F("8-MHz 65C02")); // a "flash string helper" ROM string
Code: Select all
/****************************************************************************** ;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* test LCD class - ROM Emulator mode - writing to LCD serial backpack. * ;~~~ 6502 low level 3P8B LCD backpack drivers ~ M.McLaren ~~~
******************************************************************************/ ;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#define VIAPORTB 0xA000 // ;~~~ X XXXXX XXXXX ~~~
#define VIAPORTA 0xA001 // ;~~~ X X X X X (C) 2026 ~~~
#define VIADDIRB 0xA002 // ;~~~ X X X X Mike McLaren ~~~
#define VIADDIRA 0xA003 // ;~~~ X X X X K8LH ~~~
;~~~ X X X X ~~~
#define clk 4 // index for VIA PA4 ;~~~ X X X X X CA65 v2.13.3 ~~~
#define stb 2 // index for VIA PA2 ;~~~ XXXXXXX XXXXX XXXXX ~~~
#define dat 0 // index for VIA PA0
.define dat 0 ; PA0 bit index
class LCD : public Print // use 'Print' features ******************* .define clk 1 ; PA1 bit index
{ public: // * .define stb 2 ; PA2 bit index
void init(); // * .define swx 3 ; PA3 bit index
void cmd(uint8_t); // *
size_t write(uint8_t); // * lcdInit: ; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
private: // * lda DDRA ; read current DDRA state (4)
bool rs = 0; // lcd 'RS' flag * ora #(1<<stb | 1<<clk | 1<<dat) ; (2)
}; // **************************************** sta DDRA ; clk/dat/stb pins to outputs (4)
delayCy(100*msecs) ; lcd power-on delay (100000) 1-MHz clock
void LCD::init() // **************************************** wrCmd #$38 ; 8-bit, 2-line, 5x7 font (234)
{ uReset(); // reset 6502 (synchronize uC to cpu) * wrCmd #$0C ; display on, cursor off (234)
run(0); // insert I/O into address space * wrCmd #$06 ; cursor inc, shift off (234)
wrRAM(VIADDIRA, (1 << clk) | (1 << stb) | (1 << dat)); // VIA outputs * wrCmd #$01 ; clear display (234)
_delay_ms(100); // LCD power-on delay * delayCy(2*msecs) ; required delay (2000) 1-MHz clock
cmd(0x38); // 8-bit interface, 2-lines, 5x7 font * rts ; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
cmd(0x0C); // display on, cursor & blink off *
cmd(0x06); // cursor inc, shift off * lcdCmd: ; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
cmd(0x01); // clear display * clc ; rs = 0 'command reg (2)
_delay_ms(2); // required delay * .byte $89 ; skip over 'sec' instruction (2) bit <imm>
} // **************************************** lcdDat: ; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
sec ; rs = 1 'data reg (2)
void LCD::cmd(uint8_t c) // **************************************** sta work ; (3)zp
{ rs = 0; write(c); rs = 1; // * ;~~~ ~~~
} // **************************************** ;~~~ clock 8-bit 'work' byte into the shift register ic ~~~
;~~~ ~~~
size_t LCD::write(uint8_t data) // **************************************** ldx #8 ; bit counter (2)
{ for(uint8_t i = 8; i > 0 ; i--) // load 74HC164 shift register * lda PORTA ; (4)
{ wrRAM(VIAPORTA, 0 << clk | (bool)(data & 128) << dat); // * lcd1: and #~(1<<stb | 1<<clk | 1<<dat) ; (2)
wrRAM(VIAPORTA, 1 << clk | (bool)(data & 128) << dat); // * rol work ; move bit 7 into Carry (5)zp
data <<= 1; // prep next bit * adc #$00 ; add bit, dat = 0 or 1 (2)
} // re-task 'clk' pin for LCD 'RS' input * sta PORTA ; clk = 0, dat = 0 or 1 (4)
wrRAM(VIAPORTA, rs << clk | 1 << stb); // strobe LCD 'E' pin * ora #(1<<clk) ; (2)
wrRAM(VIAPORTA, rs << clk | 0 << stb); // " * sta PORTA ; clk = 1, dat = 0 or 1 (4)
uPush(0x03); // single-cycle 'nop' (I/O work-around) * dex ; done? (2)
return 1; // * bne lcd1 ; no, branch (loop), else (2)(3)
} // **************************************** ;~~~ ~~~
;~~~ re-task 'clk' pin for LCD 'rs' input & strobe LCD ~~~
;~~~ ~~~
bit work ; rs = 1 (data)? (3)zp
bmi strobe ; yes, skip, else (2)(3)
eor #(1<<clk) ; rs = 0 (command) (2)
strobe: ora #(1<<stb) ; lcd 'e' = 1 (2)
sta PORTA ; strobe LCD 'E' pin (4)
eor #(1<<stb) ; " (2)
sta PORTA ; " (4)
rts ; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~