Need advice on VIA control (PORT A)

Since we do not have native C compilers on the Oric, this forum will be mostly be used by people using CC65 or the OSDK. But any general C related post will be welcome !
User avatar
8bit-Dude
Flying Officer
Posts: 141
Joined: Tue Mar 14, 2017 1:33 pm
Location: Japan

Need advice on VIA control (PORT A)

Post by 8bit-Dude »

DBug suggested that I come here for some advice on the VIA.

The Oric connects to the 8bit-Hub through the printer port. The trick consists of switching PORT A to input/output mode when receiving/sending data. The code does this 20 times per second, which gives a theoretical bandwidth of 5 KB/s (20*256 bytes).

The system works correctly, and it is possible to pass UDP/TCP packets to 8bit-OS and 8bit-Slicks. But after a few minutes (I would 5-10), the VIA stops toggling PORT A (the humming sound stops). A soft reset (through the CUMANA Reborn) fixes the issue, and gives another few minutes of PORT A communication. So it looks like a software issue is at play (rather than hardware).

I wonder if there is need for some register to be reset? I am pasting the CC65 Hub communication code here, hoping for some insights from veteran members... The main points of interest are the UpdateHub(), SendByte() and ReceiveByte().

Code: Select all

unsigned char tick;
unsigned char hubState[7] = { COM_ERR_OFFLINE, 255, 255, 255, 255, 80, 100 };
unsigned char sendID = 0, sendLen = 0, sendHub[256];
unsigned char recvID = 0, recvLen = 0, recvHub[256];
unsigned char packetID = 0;

unsigned char QueueHub(unsigned char packetCmd, unsigned char* packetBuffer, unsigned char packetLen)
{
	unsigned char i;
	
	// Check if there is enough space in buffer
	if (packetLen > 255-sendLen)
		return 0;
	
	// Add to send buffer
	if (++packetID > 15) { packetID = 1; }
	sendHub[sendLen++] = packetID;	
	sendHub[sendLen++] = packetLen+1;
	sendHub[sendLen++] = packetCmd;
	for (i=0; i<packetLen; i++) 
		sendHub[sendLen++] = packetBuffer[i];
	return 1;
}

void SendByte(unsigned char value)
{
	// Send 1 byte to HUB
	POKE(0x0301, value);		// Write to Printer Port
	POKE(0x0300, 175);			// Send STROBE (falling signal)
	tick++; tick++; tick++; 	        // Wait 3 cycles
	POKE(0x0300, 255);			// Reset STROBE
}

unsigned char RecvByte(unsigned char* value)
{
	// Recv 1 byte from HUB
	unsigned char i = 255;
	while (1) {  // Countdown i to 0
		if (PEEK(0x030d)&2) { *value = PEEK(0x0301); break; } 	// Look for ACKNOW on CA1 then read Printer Port
		if (!i--) return 0;
	}
	return 1;
}

void RecvHub() 
{
	unsigned char i, len, ID, packetLen;
	unsigned char header, footer, checksum;

	SendByte(85);		// Ask Hub to send us some Data
	POKE(0x0303, 0);	// Set Port A as Input
	i = PEEK(0x0301);      // Reset ORA
	
	// Check header
	if (!RecvByte(&header) || header != 170) {
		if (hubState[0] != COM_ERR_OFFLINE) { hubState[0] = COM_ERR_HEADER; }
		return;
	}
			
	// Get packet ID
	if (!RecvByte(&ID)) { hubState[0] = COM_ERR_TRUNCAT; return; }
	
	// Read joystick/mouse data
	for (i=1; i<7; i++) {
		if (!RecvByte(&hubState[i])) { hubState[0] = COM_ERR_TRUNCAT; return; }
	}

	// Get buffer length
	if (!RecvByte(&len)) { hubState[0] = COM_ERR_TRUNCAT; return; }

	// Read buffer data
	for (i=0; i<len; i++) {
		if (!RecvByte(&recvHub[i])) { hubState[0] = COM_ERR_TRUNCAT; return; }
	}	

	// Get footer
	if (!RecvByte(&footer)) { hubState[0] = COM_ERR_TRUNCAT; return; }

	// Verify checkum
	checksum = ID;
	for (i=1; i<7; i++) { checksum += hubState[i]; }
	for (i=0; i<len; i++) { checksum += recvHub[i]; }
	if (footer != checksum) { 
		hubState[0] = COM_ERR_CORRUPT; 
		return; 
	}
	hubState[0] = COM_ERR_OK;

	// Check packet reception
	if (ID != recvID) {
		// Check ID against last packet sent
		if ((ID & 0x0f) == sendID) {
			// Shift any remaining data
			packetLen = sendHub[1]+2;
			if (packetLen < sendLen) {
				memcpy(&sendHub[0], &sendHub[packetLen], sendLen-packetLen);
				sendLen -= packetLen;		
			} else {
				sendLen = 0;
			}		
		}
		
		// Check ID against last packet received
		if (len && ((ID & 0xf0) != (recvID & 0xf0)))
			recvLen = len;
		
		// Update ID
		recvID = ID;
	}
}

void SendHub()
{
	unsigned char i, checksum, packetLen;
	
	// Send Header
	SendByte(170);
	
	// Send Packet ID
	if (sendLen) { sendID = sendHub[0]; }
	checksum = (recvID & 0xf0) + sendID;
	SendByte(checksum);
	
	// Send Packet Data (if any)
	if (sendLen) {	
		packetLen = sendHub[1];
		SendByte(packetLen);
		for (i=2; i<packetLen+2; i++) {
			SendByte(sendHub[i]); 
			checksum += sendHub[i];
		}
	} else {	
		SendByte(0); 
	}
	
	// Send footer
	SendByte(checksum);
}

clock_t updateClock;

void UpdateHub() 
{			
	unsigned char i;
	
	// Throttle requests to respect refresh rate
	if (clock() < updateClock)
		return;
	updateClock = clock()+HUB_REFRESH_RATE;

	// Was hub already initialized?
	if (hubState[0] == COM_ERR_OFFLINE) {
		// Queue reset command
		if (sendHub[0] != HUB_SYS_RESET) {
			recvID = 0; recvLen = 0;
			sendID = 0; sendLen = 0;
			QueueHub(HUB_SYS_RESET, 0, 0);			
		}
	}

	__asm__("sei");		// Disable interrupts

	// Process HUB I/O
	SendHub();
	RecvHub();	

	POKE(0x0303, 255);	// Set port A as Output
	__asm__("cli");		// Resume interrupts	
}
User avatar
Dbug
Site Admin
Posts: 4437
Joined: Fri Jan 06, 2006 10:00 pm
Location: Oslo, Norway
Contact:

Re: Need advice on VIA control (PORT A)

Post by Dbug »

So, myself I suck at VIA programming (I also hated the MFP 68901 on the Atari ST), and relied on Twilighte expertise for most of that, so if nobody here can't answer, I guess we will have to ask Fabrice Frances (the Euphoric emulator creator).

For what's worth, my Oric Profiler is using the printer port to send text, so it's only "out", but the code looks like that:

Code: Select all

; A=character to send
_PrinterSendChar
 php
 sei
 sta $301		; Send A to port A of 6522
 lda $300
 and #$ef		; Set the strobe line low
 sta $300
 lda $300
 ora #$10		; Set the strobe line high
 sta $300
 plp
loop_wait 		; Wait in a loop until active transition of CA1
 lda $30d
 and #2
 beq loop_wait	; acknowledging the byte
 lda $30d
 rts

_PrinterSendCrlf
 lda #10
 jsr _PrinterSendChar
 lda #13
 jsr _PrinterSendChar 
 rts 
I guess that's the equivalent of your SendByte:

Code: Select all

void SendByte(unsigned char value)
{
	// Send 1 byte to HUB
	POKE(0x0301, value);		// Write to Printer Port
	POKE(0x0300, 175);			// Send STROBE (falling signal)
	tick++; tick++; tick++; 	// Wait 3 cycles
	POKE(0x0300, 255);			// Reset STROBE
}
The two main differences I see are:
- This is in C, so you have no control on the actual time spent (not sure how "tick++" related to "Wait 3 cycles")
- The code only writes values and does not poll the status

Bonus question to the other people: Do you know what the buzzing noise is, based on the code, do you think it is just "electronic noise" or it's actually doing things like "opening and closing the tape relay" very fast?
User avatar
iss
Wing Commander
Posts: 1637
Joined: Sat Apr 03, 2010 5:43 pm
Location: Bulgaria
Contact:

Re: Need advice on VIA control (PORT A)

Post by iss »

@Dbug: YES, the values 175=0xA0=10101111 and then 255=0xFF=11111111 are making the relay to switch on every byte being send (making noise twice on ON and on OFF) because of change in bit PB6 (STROBE is PB4 which is ok).

In common POKE-ing fixed values to PORT B (0x300) is not good idea!
The good practice is OR-ing and AND-ing bits with the current value in 0x300. Recommended is assembler routine but here is C which should work:

Code: Select all

void SendByte(unsigned char value)
{
	// Send 1 byte to HUB
	POKE(0x0301, value);		// Write to Printer Port
	*((char*)0x300) &= 0xEF; 	// 11101111 - Send STROBE (falling signal)
	tick++; tick++; tick++; 	// Wait 3 cycles
	*((char*)0x300) |= 0x10; 	// 00010000 - Reset STROBE
}
About the 'tick++' - it's definitely not 3 machine cycles but this depends on the protocol and maybe it can be optimized for lower latency - eventually the result will be faster transfer.
User avatar
Chema
Game master
Posts: 3013
Joined: Tue Jan 17, 2006 10:55 am
Location: Gijón, SPAIN
Contact:

Re: Need advice on VIA control (PORT A)

Post by Chema »

Not a via expert either, but what iss & dbug said seems sound.

It is not a good idea to modify other bits whenever you want to change one in a VIA register. Also, I would wait for a real ACK as Dbug does. Your tick++ is probably spending too many cycles (you know it is not simple inc absolute instruction, there could be promotions to integer and it should return the value of tick before being incremented, according to the C standard, that is why it is always more efficient to do preincrements).

I might be wrong, but I'd say that the combination of the two above c pi uls make you lose bits. Why the VIA stops working is beyond me.

Just one thought. If you are sending information by bitbang-ing at least the read/recv routines should be done in asm. Probably the whole send/receive packet should.
User avatar
8bit-Dude
Flying Officer
Posts: 141
Joined: Tue Mar 14, 2017 1:33 pm
Location: Japan

Re: Need advice on VIA control (PORT A)

Post by 8bit-Dude »

Thanks for the quick answer guyz!! After seeing Dbug's code, I had a feeling that manipulating just bits was the way forward.
BTW, I got the sample code from the Oric User Manual, where they show POKING values to $300.

Anyway, I will fix today and re-upload new DISC. Hopefully this will keep the VIA going beyond 10 minutes!
User avatar
8bit-Dude
Flying Officer
Posts: 141
Joined: Tue Mar 14, 2017 1:33 pm
Location: Japan

Re: Need advice on VIA control (PORT A)

Post by 8bit-Dude »

I can confirm that the fix worked. The VIA is now silent, and I was able to keep it talking to Hub continuously for 30 minutes.
Thanks a lot for the quick feedback!!!
User avatar
mikeb
Flight Lieutenant
Posts: 282
Joined: Wed Sep 05, 2018 8:03 pm
Location: West Midlands, UK
Contact:

Re: Need advice on VIA control (PORT A)

Post by mikeb »

Dbug wrote: Sun Nov 15, 2020 10:08 am ... based on the code, do you think it is just "electronic noise" or it's actually doing things like "opening and closing the tape relay" very fast?
Please don't open and close the relay very fast, you will wear it out!

Seriously, I can't recall if it was Oric or Enterprise, but one piece of software allegedley has copy-protection/anti-hack tech which would deliberately "buzz" the cassette relay to death when triggered. Seems a bad idea to me ...

The cassette relay is on Port *B*, bit 6 (1=On, 0=Off), so as long as you are hammering port A only, it should be ok.

To test if the cassette relay is inadvertantly being waggled, get a continuity tester in the extreme end pins of the DIN socket (first and last) and look for continuity!

Most likely the humming noise is the same as the usual noises from Oric prodding the VIA/PSG for keyboard scanning, which causes a change in sound from the speaker telegraphing things going on internally.
Post Reply