/*	Copyright  (c)	Günter Woigk 2013 - 2013
					mailto:kio@little-bat.de

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

	Permission to use, copy, modify, distribute, and sell this software and
	its documentation for any purpose is hereby granted without fee, provided
	that the above copyright notice appear in all copies and that both that
	copyright notice and this permission notice appear in supporting
	documentation, and that the name of the copyright holder not be used
	in advertising or publicity pertaining to distribution of the software
	without specific, written prior permission.  The copyright holder makes no
	representations about the suitability of this software for any purpose.
	It is provided "as is" without express or implied warranty.

	THE COPYRIGHT HOLDER DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
	INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
	EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY SPECIAL, INDIRECT OR
	CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
	DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
	TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
	PERFORMANCE OF THIS SOFTWARE.
*/


#include "SioInspector.h"
#include "MyLineEdit.h"
#include "Sio.h"
#include "QLabel"


static const uint dy = 18;


// Helper:
MyLineEdit* SioInspector::newLineEdit(int x,int y, int w, cstr slot)
{
	MyLineEdit* led = new MyLineEdit("",this);
	led->setFixedWidth(w);
	if(slot) QWidget::connect(led,SIGNAL(returnPressed()),this,slot);
	else	 led->setEnabled(0);
	led->move(x,y);
	return led;
}

// Helper:
QLabel* SioInspector::newLabel(int x,int y, cstr text)
{
	static QFont myfont("Monaco",12);

	QLabel* label = new QLabel(text,this);
	label->move(x,y);
	label->setFixedHeight(16);
	label->setFont(myfont);
	return label;
}

// Helper:
QLabel* SioInspector::newInfo(int x,int y)
{
	static QFont myinfofont("Arial Narrow",11);

	QLabel* label = new QLabel("Info",this);
	label->setFixedHeight(16);
	label->setFixedWidth(255);
	label->setFont(myinfofont);
	label->setStyleSheet("QLabel { color : rgb(64,64,64); }");
	label->move(x,y);
	return label;
}





SioInspector::SioInspector(QWidget* parent, Sio* sio)
:	Inspector(parent,"sio"),
	sio(sio),
	mra0(0x100),mra1(0x100),mra2(0x100),csra(0x100),sra(0x100),
	mrb0(0x100),mrb1(0x100),mrb2(0x100),csrb(0x100),srb(0x100),
	isr(0x100),imr(0x100),
	ctpu(0x100),ctpl(0x100),
	acr(0x100),gpr(0x100),
	ipr(0x100),opcr(0x100),opr(0x100),
	txa_tpc(0),
	txb_tpc(0),
	rxa_tpc(0),
	rxb_tpc(0),
	txa_rp(0),txa_wp(0),rxa_rp(0),rxa_wp(0),
	txb_rp(0),txb_wp(0),rxb_rp(0),rxb_wp(0)
{
	setWindowTitle("SIO");

	uint x0 = 4, x1=x0+36,		x2=x1+130, x3=x2+34;
	uint y  = 4;
	uint w  = 40;
	uint w2 = 80;
	uint w3 = 120;

	newLabel(x0,y,"MRA0");	led_mra0 = newLineEdit(x1,y,w,SLOT(slot_mra0()));		info_mra0 = newInfo(x1+45,y);		y += dy;
	newLabel(x0,y,"MRA1");	led_mra1 = newLineEdit(x1,y,w,SLOT(slot_mra1()));		info_mra1 = newInfo(x1+45,y);		y += dy;
	newLabel(x0,y,"MRA2");	led_mra2 = newLineEdit(x1,y,w,SLOT(slot_mra2()));		info_mra2 = newInfo(x1+45,y);		y += dy;
	newLabel(x0,y,"SRA" );	led_sra  = newLineEdit(x1,y,w,SLOT(slot_sra()));		info_sra  = newInfo(x1+45,y);		y += dy + 4;

	newLabel(x0,y,"CSRA");	led_csra = newLineEdit(x1,y,w,SLOT(slot_csra()));
	newLabel(x1+60,y,"in");	led_baudrate_rxa = newLineEdit(x1+80,y,w2,NULL);
	newLabel(x1+175,y,"out");	led_baudrate_txa = newLineEdit(x1+205,y,w2,NULL);	y += dy;

	newLabel(x0,y,"RXA" );	led_ibua = newLineEdit(x1,y,w3,SLOT(slot_ibua()));
	newLabel(x2,y,"TXA" );	led_obua = newLineEdit(x3,y,w3,SLOT(slot_obua()));		y += dy + 8;


	newLabel(x0,y,"MRB0");	led_mrb0 = newLineEdit(x1,y,w,SLOT(slot_mrb0()));		info_mrb0 = newInfo(x1+45,y);		y += dy;
	newLabel(x0,y,"MRB1");	led_mrb1 = newLineEdit(x1,y,w,SLOT(slot_mrb1()));		info_mrb1 = newInfo(x1+45,y);		y += dy;
	newLabel(x0,y,"MRB2");	led_mrb2 = newLineEdit(x1,y,w,SLOT(slot_mrb2()));		info_mrb2 = newInfo(x1+45,y);		y += dy;
	newLabel(x0,y,"SRB" );	led_srb  = newLineEdit(x1,y,w,SLOT(slot_srb()));		info_srb  = newInfo(x1+45,y);		y += dy + 4;

	newLabel(x0,y,"CSRB");	led_csrb = newLineEdit(x1,y,w,SLOT(slot_csrb()));
	newLabel(x1+60,y,"in");	led_baudrate_rxb = newLineEdit(x1+80,y,w2,NULL);
	newLabel(x1+175,y,"out");	led_baudrate_txb = newLineEdit(x1+205,y,w2,NULL);	y += dy;

	newLabel(x0,y,"RXB" );	led_ibub = newLineEdit(x1,y,w3,SLOT(slot_ibub()));
	newLabel(x2,y,"TXB" );	led_obub = newLineEdit(x3,y,w3,SLOT(slot_obub()));		y += dy + 8;

	newLabel(x0,y,"ACR" );	led_acr  = newLineEdit(x1,y,w,SLOT(slot_acr()));		info_acr = newInfo(x1+45,y);		y += dy;
	newLabel(x0,y,"IMR" );	led_imr  = newLineEdit(x1,y,w,SLOT(slot_imr()));		info_imr = newInfo(x1+45,y);		y += dy;
	newLabel(x0,y,"ISR" );	led_isr  = newLineEdit(x1,y,w,SLOT(slot_isr()));		info_isr = newInfo(x1+45,y);		y += dy;

	newLabel(x0,y,"CTP" );	led_ctp  = newLineEdit(x1,y,w,SLOT(slot_ctp()()));
	newLabel(x1+60,y,"Period" );	ct_period = newLineEdit(x1+110,y,w2,NULL);
	newLabel(x1+220,y,"GPR" );	led_gpr   = newLineEdit(x1+245,y,w,SLOT(slot_gpr()));	y += dy + 4;

	newLabel(x0,y,"IPR" );	led_ipr  = newLineEdit(x1,y,w,SLOT(slot_ipr()));		info_ipr  = newInfo(x1+45,y);		y += dy + 4;
	newLabel(x0,y,"OPCR");	led_opcr = newLineEdit(x1,y,w,SLOT(slot_opcr()));		info_opcr = newInfo(x1+45,y);		y += dy;
	newLabel(x0,y,"OPR" );	led_opr  = newLineEdit(x1,y,w,SLOT(slot_opr()));		info_opr  = newInfo(x1+45,y);		y += dy;

	setFixedHeight(y+2);
	setFixedWidth(340);
}


static cstr onoff(uint8 n) { return n&1 ? "ON" : "OFF"; }
static cstr yesno(uint8 n) { return n&1 ? "YES" : "NO"; }
//static cstr endis(uint8 n) { return n&1 ? "enabled" : "disabled"; }
#undef BIT
#define	BIT(N,B)		(((N)>>(B))&1)										// get bit value at bit position B


void SioInspector::update()
{
	static uint txtl[4] = {16,6,12,1};
	static uint rxtl[4] = {1,6,12,16};
	static cstr pari[8] = {"Even","Odd","Set","Set","None","None","Multidrop Data","Multidrop Address"};
	// MR1 Bit 4-3: Parity mode.
	//		00 = With parity (default)
	//		01 = Force parity
	//		10 = No parity
	//		11 = Multidrop mode
	// MR1 Bit-2:
	//	 In non-Multidrop mode, this bit selects the parity.
	//		0 = Even Parity (default)
	//		1 = Odd Parity
	//	 In Multidrop mode, this bit is the Address/Data flag.
	//		0 = Data (default)
	//		1 = Address
	static uint stop[8] = {563,625,688,750,813,875,938,000};
	static cstr loop[4] = {"None","Automatic echo","Local loopback","remote loopback"};

	bool ip_conf_changed = false;
	bool op_conf_changed = false;

	if(mra0!=sio->mra0 || mra1!=sio->mra1)
	{
		mra0 = sio->mra0; led_mra0->setText(usingstr("$%02X",mra0));
		mra1 = sio->mra1; led_mra1->setText(usingstr("$%02X",mra1));
		op_conf_changed = yes;

		uint rxtli = ((sio->mra0>>5)&2) + ((sio->mra1>>6)&1);	// RX trigger level index in rxtl[] table

		info_mra0->setText(usingstr(
			"WDT:%u RXTL:%u TXTL:%u BDR2:%u FTST:%u BDR1:%u",
			BIT(mra0,7),	// watch dog timer
			BIT(mra0,6),	// RX trigger level
			(mra0>>4)&3,	// TX trigger level
			BIT(mra0,2),	// Baud rate Ext. 2
			BIT(mra0,1),	// Factory test mode
			BIT(mra0,0) ));	// Baud rate Ext. 1

		led_mra0->setToolTip(usingstr(
			"RX timeout watchdog: %s\n"
			"RX trigger level: %u available\n"
			"TX trigger level: %u free\n"
			"Baudrate extended table 2: %s\n"
			"Baudrate extended table 1: %s",
			onoff(mra0>>7),
			rxtl[rxtli],
			txtl[(mra0>>4)&3],
			yesno((mra0>>2)),
			yesno((mra0>>0)) ));

		info_mra1->setText(usingstr(
			"RXRTS:%u RXTL:%u EMOD:%u PARI:%u WLEN:%u",
			BIT(mra1,7),	// RX RTS control on OP0
			BIT(mra1,6),	// RX trigger level
			BIT(mra1,5),	// Error mode
			(mra1>>2)&7,	// Parity mode&type
			(mra1>>0)&3 ));	// Word length

		led_mra1->setToolTip(usingstr(
			"OP0 used for RXA RTS control: %s\n"
			"RX trigger level: %u available\n"
			"Error mode: %s\n"
			"Parity: %s\n"
			"Word length: %u",

			yesno(mra1>>7),
			rxtl[rxtli],
			BIT(mra1,5) ? "Single character" : "Block",
			pari[(mra1>>2)&7],
			5 + (mra1&3) ));
	}

	if(mra2!=sio->mra2)
	{
		mra2 = sio->mra2;
		led_mra2->setText(usingstr("$%02X",mra2));
		ip_conf_changed = yes;
		op_conf_changed = yes;

		info_mra2->setText(usingstr(
			"LOOP:%u TXRTS:%u TXCTS:%u STOP:%u",
			(mra2>>6)&3,	// Loopback mode control
			BIT(mra2,5),	// TX RTS control on OP0
			BIT(mra2,4),	// TX CTS control on IP0
			(mra2>>0)&15));	// Stop bit length

		led_mra2->setToolTip(usingstr(
			"Loop back mode: %s\n"
			"OP0 used for TXA RTS control: %s\n"
			"IP0 used for TXA CTS control: %s\n"
			"Stop bit length: %u.%u",

			loop[(mra2>>6)&3],
			yesno(mra2>>5),
			yesno(mra2>>4),
			(((mra2>>0)&15)+1)/8,stop[(mra2>>0)&7] ));
	}

	if(sra!=sio->sra)
	{
		sra  = sio->sra;
		led_sra->setText(usingstr("$%02X",sra));

		info_sra->setText(usingstr(
			"ERR:%u TXEMP:%u TXRDY:%u RXFUL:%u RXRDY:%u",
			(sra>>4)&15,	// Break, Framing, Parity, & Overrun error
			BIT(sra,3),		// TX FIFO empty
			BIT(sra,2),		// TX ready
			BIT(sra,1),		// RX FIFO full
			BIT(sra,0)		// RX available
			));

		led_sra->setToolTip(usingstr(
			"Received Break: %s\n"
			"Framing error: %s\n"
			"Parity error: %s\n"
			"Overrun error: %s\n"
			"Transmitter buffer: %s\n"
			"Transmitter ready: %s\n"
			"Receiver buffer %s\n"
			"Receiver ready: %s",
			yesno(sra>>7),
			yesno(sra>>6),
			yesno(sra>>5),
			yesno(sra>>4),
			BIT(sra,3) ? "empty" : "not empty",
			yesno(sra>>2),
			BIT(sra,1) ? "FULL" : "not full",
			yesno(sra>>0) ));
	}

	if(mrb0!=sio->mrb0 || mrb1!=sio->mrb1)
	{
		mrb0 = sio->mrb0; led_mrb0->setText(usingstr("$%02X",mrb0));
		mrb1 = sio->mrb1; led_mrb1->setText(usingstr("$%02X",mrb1));
		op_conf_changed = yes;

		uint rxtli = ((sio->mrb0>>5)&2) + ((sio->mrb1>>6)&1);	// RX trigger level index in rxtl[] table

		info_mrb0->setText(usingstr(
			"WDT:%u RXTL:%u TXTL:%u BDR2:%u FTST:%u BDR1:%u",
			BIT(mrb0,7),	// watch dog timer
			BIT(mrb0,6),	// RX trigger level
			(mrb0>>4)&3,	// TX trigger level
			BIT(mrb0,2),	// Baud rate Ext. 2
			BIT(mrb0,1),	// Factory test mode
			BIT(mrb0,0) ));	// Baud rate Ext. 1

		led_mrb0->setToolTip(usingstr(
			"RX timeout watchdog: %s\n"
			"RX trigger level: %u available\n"
			"TX trigger level: %u free\n"
			"Baudrate extended table 2: %s\n"
			"Baudrate extended table 1: %s",
			onoff(mrb0>>7),
			rxtl[rxtli],
			txtl[(mrb0>>4)&3],
			yesno((mrb0>>2)),
			yesno((mrb0>>0)) ));

		info_mrb1->setText(usingstr(
			"RXRTS:%u RXTL:%u EMOD:%u PARI:%u WLEN:%u",
			BIT(mrb1,7),	// RX RTS control on OP0
			BIT(mrb1,6),	// RX trigger level
			BIT(mrb1,5),	// Error mode
			(mrb1>>2)&7,	// Parity mode&type
			(mrb1>>0)&3 ));	// Word length

		led_mrb1->setToolTip(usingstr(
			"OP1 used for RXB RTS control: %s\n"
			"RX trigger level: %u available\n"
			"Error mode: %s\n"
			"Parity: %s\n"
			"Word length: %u",
			yesno(mrb1>>7),
			rxtl[rxtli],
			BIT(mrb1,5) ? "Single character" : "Block",
			pari[(mrb1>>2)&7],
			5 + (mrb1&3) ));
	}

	if(mrb2!=sio->mrb2)
	{
		mrb2 = sio->mrb2;
		led_mrb2->setText(usingstr("$%02X",mrb2));
		ip_conf_changed = yes;
		op_conf_changed = yes;

		info_mrb2->setText(usingstr(
			"LOOP:%u TXRTS:%u TXCTS:%u STOP:%u",
			(mrb2>>6)&3,	// Loopback mode control
			BIT(mrb2,5),	// TX RTS control on OP0
			BIT(mrb2,4),	// TX CTS control on IP0
			(mrb2>>0)&15));	// Stop bit length

		led_mrb2->setToolTip(usingstr(
			"Loop back mode: %s\n"
			"OP1 used for TXB RTS control: %s\n"
			"IP1 used for TXB CTS control: %s\n"
			"Stop bit length: %u.%u",
			loop[(mrb2>>6)&3],
			yesno(mrb2>>5),
			yesno(mrb2>>4),
			(((mrb2>>0)&15)+1)/8,stop[(mrb2>>0)&7] ));
	}

	if(srb!=sio->srb)
	{
		srb  = sio->srb;
		led_srb->setText(usingstr("$%02X",srb));

		info_srb->setText(usingstr(
			"ERR:%u TXEMP:%u TXRDY:%u RXFUL:%u RXRDY:%u",
			(srb>>4)&15,	// Break, Framing, Parity, & Overrun error
			BIT(srb,3),		// TX FIFO empty
			BIT(srb,2),		// TX ready
			BIT(srb,1),		// RX FIFO full
			BIT(srb,0)		// RX available
			));

		led_srb->setToolTip(usingstr(
			"Received Break: %s\n"
			"Framing error: %s\n"
			"Parity error: %s\n"
			"Overrun error: %s\n"
			"Transmitter buffer: %s\n"
			"Transmitter ready: %s\n"
			"Receiver buffer: %s\n"
			"Receiver ready: %s",
			yesno(srb>>7),
			yesno(srb>>6),
			yesno(srb>>5),
			yesno(srb>>4),
			BIT(srb,3) ? "empty" : "not empty",
			yesno(srb>>2),
			BIT(srb,1) ? "FULL" : "not full",
			yesno(srb>>0) ));
	}

	if(acr!=sio->acr)	// aux. control register
	{
		/*	ACR Bits 3-0:
			This field selects which bits of the input port change register (IPCR) cause the interrupt status register (ISR) bit-7 to be set.
			For example, if bit-0 = 1, then a change of state in IP0 will set ISR bit-7.
			If bit-0 and bit-2 are both '1', then whenever IP0 or IP2 changes state, ISR bit-7 will be set.
				0 = Disabled (default)
				1 = Enabled
			ACR Bits 6-4:
			Counter/Timer Mode and Clock Source. These bits should not be altered while the C/T is in use. Prior to changing these bits, the C/T must be stopped if in counter mode.
			If the C/T is in timer mode, its output must be disabled and its interrupt must be masked. see table ctmode[].
			ACR Bit-7: Baud rate table Select.
			This bit is used to select between two sets of baud rate tables. See Baudrate table on Page 18. It should be changed only after both channels have been reset and disabled.
				0 = Set 1
				1 = Set 2
		*/
		static cstr ctmode[8] = {
			"Counter, External (IP2)",
			"Counter, TXAClk1 - Transmit A clock",
			"Counter, TXBClk1 - Transmit B clock",
			"Counter, External (XTAL1/Clk) / 16",
			"Timer, External (IP2)",
			"Timer, External (IP2) / 16",
			"Timer, External (XTAL1/Clk)",
			"Timer, External (XTAL1/Clk) / 16" };

		acr = sio->acr;
		led_acr->setText(usingstr("$%02X",acr));
		ip_conf_changed = yes;

		info_acr->setText(usingstr(
			"BDR0:%u CTMOD:%u IPISR:%s",
			BIT(acr,7) + 1,
			(acr>>4)&7,
			binstr(acr,"0000","1111")
			));

		led_acr->setToolTip(usingstr(
			"Baud rate table: %u\n"
			"Counter/Timer mode: %s\n"
			"Which input ports set ISR.bit7:%s%s%s%s",
			BIT(acr,7) + 1,
			ctmode[(acr>>4)&7],
			BIT(acr,3) ? " IP3" : "",
			BIT(acr,2) ? " IP2" : "",
			BIT(acr,1) ? " IP1" : "",
			BIT(acr,0) ? " IP0" : acr&15 ? "" : " none"
		));
	}

	if(isr!=sio->isr)
	{
		/*	ISR Bit-0: Transmit ready A.
				This bit is set when channel A's transmit buffer (FIFO) is filled below the programmed transmit trigger level (see MR0A bits 5-4).
				This bit can be cleared by loading the TX FIFO above the trigger level.
			ISR Bit-1: Receive ready A .
				This bit is set when channel A's receive buffer (FIFO) is filled above the programmed receive trigger level condition (see MR0A bit-6 and MR1A bit-6).
				This bit can be cleared by reading the data out of the FIFO till it falls below the trigger level.
			ISR Bit-2: Channel A change in break.
				This bit is set when channel A receiver detects the beginning or the end of a break condition.
				It is reset when the CPU issues a channel A reset break change interrupt command (CRA bits 7-4 = 0x5).
			ISR Bit-3: Counter/Timer (C/T) ready.
				In counter mode, this bit is set when the C/T reaches terminal count.
				In timer mode, this bit is set each time the C/T output switches from low to high (rising edge - see Figure 2).
				In either mode, this bit is cleared by a stop counter command.
			ISR Bit-4: Transmit ready B.
				This bit is set when channel B's transmit buffer (FIFO) is filled below the programmed transmit trigger level (see MR0B bits 5-4).
				This bit can be cleared by loading the TX FIFO above the trigger level.
			ISR Bit-5: Receive ready B.
				This bit is set when channel B's receive buffer (FIFO) is filled above the programmed receive trigger level condition (see MR0B bit-6 and MR1B bit-6).
				This bit can be cleared by reading the data out of the FIFO till it falls below the trigger level.
			ISR Bit-6. Channel B change in break.
				This bit is set when channel B receiver detects the beginning or the end of a break condition.
				It is reset when the CPU issues a channel B reset break change interrupt command (CRB bits 7-4 = 0x5).
			ISR Bit-7. Input port change status.
				This bit is set when a change of state has occurred at the IP0, IP1, IP2, or IP3 inputs, and that event has been enabled to cause an interrupt by programming ACR Bits 3-0.
				This bit is cleared when the CPU reads the input port change register.
		*/
		isr = sio->isr;
		led_isr->setText(usingstr("$%02X",isr));

		info_isr->setText(usingstr(
			"IPCH:%u BRKB:%u RXRDYB:%u TXRDYB:%u CTRDY:%u BRKA:%u RXRDYA:%u TXRDYA:%u",
			BIT(isr,7), BIT(isr,6), BIT(isr,5), BIT(isr,4),
			BIT(isr,3), BIT(isr,2), BIT(isr,1), BIT(isr,0)
		));

		led_isr->setToolTip(usingstr(
			"Input port change: %s\n"
			"B: Break change: %s\n"
			"B: Receiver ready: %s\n"
			"B: Transmitter ready: %s\n"
			"Counter/timer ready: %s\n"
			"A: Break change: %s\n"
			"A: Receiver ready: %s\n"
			"A: Transmitter ready: %s",
			yesno(isr>>7), yesno(isr>>6), yesno(isr>>5), yesno(isr>>4),
			yesno(isr>>3), yesno(isr>>2), yesno(isr>>1), yesno(isr>>0)
		));
	}

	if(imr!=sio->imr)
	{
		imr = sio->imr;
		led_imr->setText(usingstr("$%02X",imr));

		info_imr->setText(usingstr(
			"IPCH:%u BRKB:%u RXRDYB:%u TXRDYB:%u CTRDY:%u BRKA:%u RXRDYA:%u TXRDYA:%u",
			BIT(isr,7), BIT(isr,6), BIT(isr,5), BIT(isr,4),
			BIT(isr,3), BIT(isr,2), BIT(isr,1), BIT(isr,0)
		));

		led_imr->setToolTip(usingstr(
			"Input port change: %s\n"
			"B: Break change: %s\n"
			"B: Receiver ready: %s\n"
			"B: Transmitter ready: %s\n"
			"Counter/timer ready: %s\n"
			"A: Break change: %s\n"
			"A: Receiver ready: %s\n"
			"A: Transmitter ready: %s",
			onoff(imr>>7), onoff(imr>>6), onoff(imr>>5), onoff(imr>>4),
			onoff(imr>>3), onoff(imr>>2), onoff(imr>>1), onoff(imr>>0)
		));
	}

	if(opcr!=sio->opcr)		// output port configuration register
	{
		opcr = sio->opcr;
		led_opcr->setText(usingstr("$%02X",opcr));
		op_conf_changed = yes;

		static cstr op3[4] = { "set & cleared by SOPR/ROPR", "CT output", "TXB clock", "RXB clock" };
		static cstr op2[4] = { "set & cleared by SOPR/ROPR", "TXA clock x 16", "TXA clock", "RXA clock" };

		info_opcr->setText(usingstr(
			"OP7:%u OP6:%u OP5:%u OP4:%u OP3:%u OP2:%u",
			BIT(opcr,7),
			BIT(opcr,6),
			BIT(opcr,5),
			BIT(opcr,4),
			(opcr>>2)&3,
			(opcr>>0)&3
		));

		led_opcr->setToolTip(usingstr(
			"OP7: %s\n"
			"OP6: %s\n"
			"OP5: %s\n"
			"OP4: %s\n"
			"OP3: %s\n"
			"OP2: %s",
			BIT(opcr,7) ? "TXB ready (inverted ISR bit 4)" : "set & cleared by SOPR/ROPR",
			BIT(opcr,6) ? "TXA ready (inverted ISR bit 0)" : "set & cleared by SOPR/ROPR",
			BIT(opcr,5) ? "RXB ready (inverted ISR bit 5)" : "set & cleared by SOPR/ROPR",
			BIT(opcr,4) ? "RXA ready (inverted ISR bit 1)" : "set & cleared by SOPR/ROPR",
			op3[(opcr>>2)&3],
			op2[(opcr>>0)&3]
		));
	}

	if(opr!=sio->opr || op_conf_changed)	// output port register
	{
		opr = sio->opr;
		led_opr->setText(usingstr("$%02X",opr));

		static cstr op3[4] = { "OP3", "OP3 (CT output)", "OP3 (TXB clock)", "OP3 (RXB clock)" };
		static cstr op2[4] = { "OP2", "OP2 (TXA clock x 16)", "OP2 (TXA clock)", "OP2 (RXA clock)" };

		info_opr->setText(usingstr(
			"OP7:%u OP6:%u OP5:%u OP4:%u OP3:%u OP2:%u OP1:%u OP0:%u",
			BIT(opr,7),BIT(opr,6),BIT(opr,5),BIT(opr,4),
			BIT(opr,3),BIT(opr,2),BIT(opr,1),BIT(opr,0)
		));

		led_opr->setToolTip(usingstr(
			"%s: %u\n"
			"%s: %u\n"
			"%s: %u\n"
			"%s: %u\n"
			"%s: %u\n"
			"%s: %u\n"
			"%s: %u\n"
			"%s: %u",
			BIT(opcr,7) ? "OP7 (TXBRDY)" : "OP7", BIT(opr,7),
			BIT(opcr,6) ? "OP6 (TXARDY)" : "OP6", BIT(opr,6),
			BIT(opcr,5) ? "OP5 (RXBRDY)" : "OP5", BIT(opr,5),
			BIT(opcr,4) ? "OP4 (RXARDY)" : "OP4", BIT(opr,4),
			op3[(opcr>>2)&3], BIT(opr,3),
			op2[(opcr>>0)&3], BIT(opr,2),
			BIT(mrb1,7) ? "OP1 (RXB RTS)" : BIT(mrb2,5) ? "OP1 (TXB RTS)" : "OP1", BIT(opr,1),
			BIT(mra1,7) ? "OP0 (RXA RTS)" : BIT(mra2,5) ? "OP0 (TXA RTS)" : "OP0", BIT(opr,0)
		));
	}

	if(ipr!=sio->ipr || ip_conf_changed)	// input port register
	{
		ipr = sio->ipr;
		led_ipr->setText(usingstr("$%02X",ipr));

		info_ipr->setText(usingstr(
			"IP6:%u IP5:%u IP4:%u IP3:%u IP2:%u IP1:%u IP0:%u",
			BIT(ipr,6),BIT(ipr,5),BIT(ipr,4),
			BIT(ipr,3),BIT(ipr,2),BIT(ipr,1),BIT(ipr,0)
		));

		led_ipr->setToolTip(usingstr(
			"%s: %u\n"
			"%s: %u\n"
			"%s: %u\n"
			"%s: %u\n"
			"%s: %u\n"
			"%s: %u\n"
			"%s: %u",
			((csrb>>4)&15)==15 ? "IP6 (RXB ext. clock)" : ((csrb>>4)&15)==14 ? "IP6 (RXB ext. clock / 16)" : "IP6", BIT(ipr,6),
			((csrb>>0)&15)==15 ? "IP5 (TXB ext. clock)" : ((csrb>>0)&15)==14 ? "IP5 (TXB ext. clock / 16)" : "IP5", BIT(ipr,5),
			((csra>>4)&15)==15 ? "IP4 (RXA ext. clock)" : ((csra>>4)&15)==14 ? "IP4 (RXA ext. clock / 16)" : "IP4", BIT(ipr,4),
			((csra>>0)&15)==15 ? "IP3 (TXA ext. clock)" : ((csra>>0)&15)==14 ? "IP3 (TXA ext. clock / 16)" : "IP3", BIT(ipr,3),
			((acr>>4)&7)==0 ? "IP2 (CT counter clock)" : ((acr>>4)&7)==4 ? "IP2 (CT timer clock)" : ((acr>>4)&7)==5 ?  "IP2 (CT timer clock / 16)" : "IP2", BIT(ipr,2),
			BIT(mrb2,4) ? "IP1 (CTS B)" : "IP1", BIT(ipr,1),
			BIT(mra2,4) ? "IP0 (CTS A)" : "IP0", BIT(ipr,0)
		));
	}

	if(ctpu!=sio->ctpu || ctpl!=sio->ctpl)	// counter/timer period
	{
		ctpu = sio->ctpu; ctpl = sio->ctpl;
		led_ctp->setText(usingstr("$%04X",ctpu*256+ctpl));
		ct_period->setText(usingstr("%8fs",sio->ct_period));
	}

	if(csra!=sio->csra) { csra = sio->csra; led_csra->setText(usingstr("$%02X",csra)); }	// clock select register a
	if(csrb!=sio->csrb)	{ csrb = sio->csrb; led_csrb->setText(usingstr("$%02X",csrb)); }	// clock select register b
	if(gpr!=sio->gpr)   { gpr  = sio->gpr;  led_gpr->setText(usingstr("$%02X",gpr)); }		// general purpose register

	// baud rates:
	if(txa_tpc!=sio->txa.tpc) { txa_tpc = sio->txa.tpc; led_baudrate_txa->setText(usingstr("%u bps",(uint)(10/txa_tpc+0.5))); }
	if(txb_tpc!=sio->txb.tpc) { txb_tpc = sio->txb.tpc; led_baudrate_txb->setText(usingstr("%u bps",(uint)(10/txb_tpc+0.5))); }
	if(rxa_tpc!=sio->rxa.tpc) { rxa_tpc = sio->rxa.tpc; led_baudrate_rxa->setText(usingstr("%u bps",(uint)(10/rxa_tpc+0.5))); }
	if(rxb_tpc!=sio->rxb.tpc) { rxb_tpc = sio->rxb.tpc; led_baudrate_rxb->setText(usingstr("%u bps",(uint)(10/rxb_tpc+0.5))); }

	// FIFO contents:
	if(txa_rp!=sio->txa.irp || txa_wp!=sio->txa.iwp) { txa_rp = sio->txa.irp; txa_wp = sio->txa.iwp; led_obua->setText(sio->txa.ibustr()); }
	if(txb_rp!=sio->txb.irp || txb_wp!=sio->txb.iwp) { txb_rp = sio->txb.irp; txb_wp = sio->txb.iwp; led_obub->setText(sio->txb.ibustr()); }
	if(rxa_rp!=sio->rxa.irp || rxa_wp!=sio->rxa.iwp) { rxa_rp = sio->rxa.irp; rxa_wp = sio->rxa.iwp; led_ibua->setText(sio->txa.ibustr()); }
	if(rxb_rp!=sio->rxb.irp || rxb_wp!=sio->rxb.iwp) { rxb_rp = sio->rxb.irp; rxb_wp = sio->rxb.iwp; led_obua->setText(sio->txb.ibustr()); }

}


void SioInspector::slot_mra0(){}
void SioInspector::slot_mra1(){}
void SioInspector::slot_mra2(){}
void SioInspector::slot_csra(){}
void SioInspector::slot_sra(){}
void SioInspector::slot_ibua(){}
void SioInspector::slot_obua(){}

void SioInspector::slot_mrb0(){}
void SioInspector::slot_mrb1(){}
void SioInspector::slot_mrb2(){}
void SioInspector::slot_csrb(){}
void SioInspector::slot_srb(){}
void SioInspector::slot_ibub(){}
void SioInspector::slot_obub(){}

void SioInspector::slot_isr(){}
void SioInspector::slot_imr(){}
void SioInspector::slot_ctp(){}
void SioInspector::slot_ctc(){}
void SioInspector::slot_acr(){}
void SioInspector::slot_gpr(){}
void SioInspector::slot_ipcr(){}
void SioInspector::slot_ipr(){}
void SioInspector::slot_opcr(){}
void SioInspector::slot_opr(){}

















