6821 PIA - reading pins

Post Reply
User avatar
Michael
Posts: 39
Joined: Thu Jun 20, 2024 10:34 am
Location: New Zealand / Australia
Contact:

6821 PIA - reading pins

Post by Michael »

It seems like the simplest thing but I've had no joy trying to read from PA0..PA7 (or PB0..PB7 but I figure that's because of the sound chip).

My initialization code now looks something like this (6502 assembly):

Code: Select all


    // Motorola 6821 PIA (Peripheral Interface Adapter)
    const uint PORTA                = 0xF010; // Peripheral A Data Register
    const uint PORTB                = 0xF011; // Peripheral B Data Register
    const uint CRA                  = 0xF012; // Control Register A
    const uint CRB                  = 0xF013; // Control Register B
    // Data Direction Registers
    const uint DDRA                 = 0xF010; // Data Direction Register A (Shared with PORTA)
    const uint DDRB                 = 0xF011; // Data Direction Register B (Shared with PORTB)



        // Motorola 6821 PIA
        // soft reset by zeroing all 6 registers (like a hard reset would do)
        
        //LDA #0b00000000 // Select DDRA
        LDA #0b00110000 // Bits 5-3=110 (CA2 output high), bit 2=0 (select DDRA)
        STA CRA
        
        LDA #0b00000000 // Set all pins of PORTA as inputs (DDRA)
        STA DDRA
        
        //LDA #0b00000100 // Select PRA (PORTA) and clear interrupt flags in CRA
        LDA #0b00110100 // Bits 5-3=110 (CA2 output high), bit 2=1 (select PORTA)
        STA CRA
        
        LDA PORTA        // READ to clear interrupt flags! 
        
        LDA #0b00000000 // Clear all output latches
        STA PORTA       // This writes to output register even though pins are inputs
        
        LDA #0b00000000 // Select DDRB
        STA CRB
        
        LDA #0b00000000 // Set all pins of PORTB as inputs (DDRB)
        STA DDRB
        
        LDA #0b00000100 // Select PRB (PORTB) and clear interrupt flags in CRB
        STA CRB
        
        LDA PORTB       // READ to clear interrupt flags!
        
        LDA #0b00000000 // Clear all output latches
        STA PORTB       // This writes to output register even though pins are inputs
The code for setting pin mode, reading and writing from Hopper seems a little complicated compared to the 65C22 VIA version:

Code: Select all

    PinMode(byte pin, PinModeOption pinMode)
    {
        uint ddr = (pin <= 7) ? DDRA : DDRB;
#ifdef M6821_PIA
        uint cr  = (pin <= 7) ? CRA  : CRB;
        // Select the DDR
        byte crValue = (pin <= 7) ? 0b00110000 : 0b00000000;
        Memory.WriteByte(cr, crValue);
#endif        
        pin = pin & 0b00000111;
        pin = 1 << pin;
        if (pinMode == PinModeOption.Input)
        {
            Memory.WriteByte(ddr, Memory.ReadByte(ddr) & ~pin);
        }
        else
        {
            Memory.WriteByte(ddr, Memory.ReadByte(ddr) | pin);
        }
    }
    
    bool DigitalRead(byte pin)
    {
        uint port = (pin <= 7) ? PORTA : PORTB;
#ifdef M6821_PIA
        // Select the port register
        uint cr   = (pin <= 7) ? CRA   : CRB;
        byte crValue = (pin <= 7) ? 0b00110100 : 0b00000100;
        Memory.WriteByte(cr, crValue);
#endif   
        byte b = Memory.ReadByte(port);
        WriteLn(b.ToString());
        return ((Memory.ReadByte(port) & (1 << (pin & 0b00000111))) != 0);
    }
    DigitalWrite(byte pin, bool value)
    {
        uint port = (pin <= 7) ? PORTA : PORTB;
#ifdef M6821_PIA
        // Select the port register
        uint cr      = (pin <= 7) ? CRA   : CRB;
        byte crValue = (pin <= 7) ? 0b00110100 : 0b00000100;
        Memory.WriteByte(cr, crValue);
        
        pin = 1 << (pin & 0b00000111);
        if (port == PORTB)
        {
            if (value)
            {
                portBShadow = portBShadow | pin;
            }
            else
            {
                portBShadow = portBShadow & ~pin;
            }
            Memory.WriteByte(port, portBShadow);
        }
        else
        {
            if (value)
            {
                Memory.WriteByte(port, Memory.ReadByte(port) | pin);
            }
            else
            {
                Memory.WriteByte(port, Memory.ReadByte(port) & ~pin);
            }
        }
#else
        pin = 1 << (pin & 0b00000111);
        if (value)
        {
            Memory.WriteByte(port, Memory.ReadByte(port) | pin);
        }
        else
        {
            Memory.WriteByte(port, Memory.ReadByte(port) & ~pin);
        }
#endif
    }
I'd appreciate a pointer to some sample code showing how to do simple reads from these pins.

Thanks,
Michael
User avatar
djrm
Posts: 160
Joined: Wed Aug 21, 2024 9:40 pm
Location: Rillington / UK
Contact:

Re: 6821 PIA - reading pins

Post by djrm »

Greetings Michael,

I think you have made a mistake in your port address definitions in the source code shown above.
Looking at Greg's example 6821 code on Github I see this:

Code: Select all

PIA		EQU	$C0E0		; MC6821 PIA base address
PIA_PRTA	EQU	PIA		; MC6821 PIA Port A & DDR A address
PIA_CTLA	EQU	PIA+1		; MC6821 PIA Control Register A address
but you have this:

Code: Select all

    const uint PORTA                = 0xF010; // Peripheral A Data Register
    const uint PORTB                = 0xF011; // Peripheral B Data Register
    const uint CRA                  = 0xF012; // Control Register A
    const uint CRB                  = 0xF013; // Control Register B
    // Data Direction Registers
    const uint DDRA                 = 0xF010; // Data Direction Register A (Shared with PORTA)
    const uint DDRB                 = 0xF011; // Data Direction Register B (Shared with PORTB)
Where you have both A registers are staggered rather in sequence,
i.e. AABB instead of ABAB
does that make sense?
Could this be your problem.
I think the unlock sequence is correct but accessing the wrong registers.
The AIM-65 display uses a similar set of register definitions to Grags:

Code: Select all

; REGISTERS FOR DISPLAY (6520)
       *=MECB_DISPLAY
RA     .BLOCK 1        ;REGISTER A
CRA    .BLOCK 1        ;CONTROL REG A
RB     .BLOCK 1        ;REG B
CRB    .BLOCK 1        ;CONTROL REG B
hth David.
User avatar
Michael
Posts: 39
Joined: Thu Jun 20, 2024 10:34 am
Location: New Zealand / Australia
Contact:

Re: 6821 PIA - reading pins

Post by Michael »

Well, that explains a lot.

Thanks David. I'll take another swing at it now ..
User avatar
Michael
Posts: 39
Joined: Thu Jun 20, 2024 10:34 am
Location: New Zealand / Australia
Contact:

Re: 6821 PIA - reading pins

Post by Michael »

Success.

Instructions to get the Hopper 6502 BIOS (and file storage) up and running on MECB 6502:
https://www.youtube.com/watch?v=nA1jQ1Mtr4c
User avatar
Editor
Posts: 363
Joined: Fri Nov 17, 2023 10:36 pm
Contact:

Re: 6821 PIA - reading pins

Post by Editor »

Michael wrote: Fri Oct 03, 2025 3:49 am Success.
Hi Michael. Sorry, I just noticed your post. The forum doesn’t seem to always notify me.

Thanks David @djrm for clarifying.

I’ll just add that it was indeed reasonably common for designers to connect the MC6821 PIA’s RS0 and RS1 pins to A1 & A0 respectively (instead of A0 & A1 respectively), as that allowed 16 bit store instructions to write to both the A & B port data registers, with a single instruction.
i.e. In a staggered ABAB register sequence (as David describes).

Note, from memory the 65C22 implements the RS0 and RS1 pins in a similar staggered (but reversed) BABA sequence.

For user experimentation clarity, I chose to follow the original Motorola MC6821 datasheet logic table documentation and intuitively connect RS0 to A0 and RS1 to A1, leading to the AABB sequence.

So, in summary, it’s definately easy to see why you initially implemented the incorrect register sequence, especially coming from the 65C22. :geek:
Post Reply