VIA anomaly ~ single-clock single-step (blind interface)

For discussing the 65xx hardware itself or electronics projects.
Post Reply
User avatar
Michael
Posts: 633
Joined: 13 Feb 2013
Location: Michigan, USA

VIA anomaly ~ single-clock single-step (blind interface)

Post by Michael »

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.
6502 Blind Interface Notes 1.png
6502 RE #2a.png
Last edited by Michael on Wed Feb 25, 2026 9:40 am, edited 3 times in total.
User avatar
Dr Jefyll
Posts: 3525
Joined: 11 Dec 2009
Location: Ontario, Canada
Contact:

Re: VIA anomaly ~ single-clock single-step (blind interface)

Post by Dr Jefyll »

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.
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.

-- 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
SamCoVT
Posts: 344
Joined: 13 May 2018

Re: VIA anomaly ~ single-clock single-step (blind interface)

Post by SamCoVT »

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.
User avatar
BigDumbDinosaur
Posts: 9425
Joined: 28 May 2009
Location: Midwestern USA (JB Pritzker’s dystopia)
Contact:

Re: VIA anomaly ~ single-clock single-step (blind interface)

Post by BigDumbDinosaur »

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.
True dat, but if Ø2 is halted in the high phase and the VIA’s control inputs are manipulated by the glue logic, the VIA has no frame of reference and I would expect it to be unresponsive.  The VIA is a synchronous device and must be clocked in order to function.  Sam points that out as well in his reply.
x86?  We ain't got no x86.  We don't NEED no stinking x86!
User avatar
Michael
Posts: 633
Joined: 13 Feb 2013
Location: Michigan, USA

Re: VIA anomaly ~ single-clock single-step (blind interface)

Post by Michael »

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...

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                 ; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6502 Blind Interface Notes 2.png
LCD Backpack SWX.png
Post Reply