Friday, 5 December 2008

ALU John, Gotta New Motor?

Over the last couple of days I have been concentrating on getting those pesky ADC and SBC instructions coded and working. Quite probably the most complex and subtle of the 6502 instructions, these two are the gateway into the really serious bit of silicon at the heart of the chip - the ALU, or Arithmetic Logic Unit.

It's this bit of the processor that is responsible for being able to do the very basic mathematical operations involved in addition and subtraction, and for keeping track of situations when the results of those operations exceed the 8-bit capacity of the Accumulator (which is the register involved). In cases where a 'carry' is needed to indicate a continuation of the calculation, or an overflow occurs when a result won't physically fit into the Accumulator, the ALU has the task of setting and/or reading flags in the PSR that will influence a calculation or report on it's outcome. In short, ADC and SBC are complicated little so-and-so's, and it's been a real headache getting them to a satisfactory point.

In fact, it's the first time since that silly VIC-20 Kernal ROM loop problem I had way back that I've needed to ask for help. There's no shame in that - these instructions have tested many a 6502 programmer and emulator writer, just because of their subtleties. Apart from any other considerations, these instructions are the only ones that work differently if the processor is in Decimal mode, so we're effectively implementing not two but four instructions here, as there are two code paths through each. Add to that the fact that there's quite a lot of information out there on the Net that talks about things like 'What the V flag does', but that quite a number of those information sources contradict one another, or in some cases are actually downright wrong, and you can begin to understand how frustrating it can be to get the details right.

Anyway, after a bit of head-scratching and some guidance from the good people at YakYak and Denial, I finally got to a place where I think the logic for these instructions is right - they add and subtract, handle the Carry flag, and (I think) set the Overflow flag when appropriate - in both Binary and Decimal mode. Now, ironically, I will probably have to revisit them in the future as there are a couple of bugs in the real CPU that I'm not emulating yet - some flags are set incorrectly in Decimal mode, for example - but for now they seem to be doing the right things, and I'm moving onwards. Wanna see what the ADC code looks like in it's finished form? Here you go:
case 105: // ADC Immediate
_memTemp = _mem[++_PC.Contents];
_TR.Contents = _AC.Contents + _memTemp + _SR[_BIT0_SR_CARRY];
if (_SR[_BIT3_SR_DECIMAL] == 1)
{
if (((_AC.Contents ^ _memTemp ^ _TR.Contents) & 0x10) == 0x10)
{
_TR.Contents += 0x06;
}
if ((_TR.Contents & 0xf0) > 0x90)
{
_TR.Contents += 0x60;
}
}
_SR[_BIT6_SR_OVERFLOW] = ((_AC.Contents ^ _TR.Contents) & (_memTemp ^ _TR.Contents) & 0x80) == 0x80 ? 1 : 0;
_SR[_BIT0_SR_CARRY] = (_TR.Contents & 0x100) == 0x100 ? 1 : 0;
_SR[_BIT1_SR_ZERO] = _TR.Contents == 0 ? 1 : 0;
_SR[_BIT7_SR_NEGATIVE] = _TR[_BIT7_SR_NEGATIVE];
_AC.Contents = _TR.Contents & 0xff;
break;
This afternoon I burned through another batch of instructions as the monitor spat 'Unable to decode' messages at me when it stumbled across VIC-20 ROM instructions the core hadn't seen before. So we can now do some ASLs and ROLs, as well as PHP and PLP. To finish, here's a recap on that instruction roll-call I posted a while back:
ASL Zero Page            executed: 4
PHP Implied executed: 1
ORA Immediate executed: 73
ASL Accumulator executed: 2
ORA Absolute executed: 51
BPL Relative executed: 1162
ASL Zero Page,X executed: 1
CLC Implied executed: 3232
JSR Absolute executed: 7513
BIT Zero Page executed: 1
ROL Zero Page executed: 16
PLP Implied executed: 1
AND Immediate executed: 168
BMI Relative executed: 7
SEC Implied executed: 5
EOR Zero Page executed: 1
LSR Zero Page executed: 1
PHA Implied executed: 97
EOR Immediate executed: 2
JMP Absolute executed: 59
LSR Zero Page,X executed: 1
CLI Implied executed: 25
RTS Implied executed: 7509
ADC Zero Page executed: 1
PLA Implied executed: 97
ADC Immediate executed: 138
ROR Accumulator executed: 4096
JMP Indirect executed: 25
SEI Implied executed: 1
STY Zero Page executed: 22
STA Zero Page executed: 343
STX Zero Page executed: 20
DEY Implied executed: 1045
TXA Implied executed: 7214
STY Absolute executed: 4
STA Absolute executed: 23
STX Absolute executed: 10
BCC Relative executed: 3173
STA Indirect Indexed,Y executed: 20533
STY Zero Page,X executed: 48
STA Zero Page,X executed: 291
TYA Implied executed: 49
STA Absolute,Y executed: 33
TXS Implied executed: 1
STA Absolute,X executed: 540
LDY Immediate executed: 65
LDX Immediate executed: 41
LDY Zero Page executed: 49
LDA Zero Page executed: 7411
LDX Zero Page executed: 57
TAY Implied executed: 50
LDA Immediate executed: 13396
TAX Implied executed: 7199
LDY Absolute executed: 2
LDA Absolute executed: 7
LDX Absolute executed: 24
BCS Relative executed: 4178
LDA Indirect Indexed,Y executed: 7254
LDY Zero Page,X executed: 6
LDA Zero Page,X executed: 53
LDA Absolute,Y executed: 32
LDA Absolute,X executed: 108
CPY Immediate executed: 2
CPY Zero Page executed: 3
CMP Zero Page executed: 156
INY Implied executed: 57
CMP Immediate executed: 148
DEX Implied executed: 214
BNE Relative executed: 18974
CMP Indirect Indexed,Y executed: 11264
CLD Implied executed: 1
CMP Absolute,X executed: 1
CPX Immediate executed: 53
SBC Zero Page executed: 3
INC Zero Page executed: 7220
INX Implied executed: 313
SBC Immediate executed: 1
NOP Implied executed: 27
BEQ Relative executed: 7403
You can also see how many of each instruction the core has executed as it tore through the ROM and initialised the (fictitious) VIC-20. Not bad, eh?

0 comments: