Thursday, 18 December 2008

What's in a Name?

The Sharp6502 IDE continues to take shape - I've been chugging along writing WinForms code for the last couple of days, getting things like standard menu-handling and form control logic in place. It's all been pretty straight-forward, aside from one little glitch that had me stumped for a while.

The scenario is this: we want a nice, clean, efficient bit of code that can be called by any menu item (on the main MDI form) that needs to open a child window. By passing such a method an identifier, the menu Click event can tell it which of a variety of child windows it is asking to be opened. Thus, by making the identifier a string in the menu item Tag field, the exact same window-opening code can be called by any menu item; the actual window that opens will be whichever one the identifier indicated. There's probably a better way of doing this, but it's a technique that's reasonably easy to understand and maintain, and has served me well for the last decade or so.

Anyway, in some cases we might not want to open a window if one already exists - a lot of the child windows can be open multiple times, but just occasionally we want a single instance of a particular one. So the window-open code has a loop to scan through the extant child windows, and it makes a note if an instance of the window it's being asked to open is already present. Then when we come to actually create and show the window, for those that must be single-instance we check the flag to see if there already is one, and just activate it if there is (or create it if there isn't).

To figure-out if a window is already present, we simply compare the names of all the forms in the MDIChildren collection with the identifier the menu item gave us - those identifiers are just the form name the menu item wants to open, so we can very easily tell if there's already one in existence. And it's this little test that had me scratching my head for a good half-an-hour last night, because although it worked for most of the windows, there was one it just would not play ball with - I never managed to get a match on name, so even though I only wanted a single instance, I always got another window opened. Highly confusing, and not a little irritating.

I carefully checked that the form name I had stashed as an identifier in the menu item Tag was right - it was. I checked that the identifier made it through to the instance-checking code intact and unmangled in some peculiar way - it did. I made sure that the comparison was checking Form.Name against the identifier - it was. I looked at what was in Form.Name in the cases where it worked - it matched. I looked at the contents of Form.Name in the case where it didn't work - and it didn't match! WTF?

Somehow, the form name (which should have been reported as 'frmXXX') was actually coming back as just 'XXX'. So my identifier, containing 'frmXXX', didn't match, and the application then went on and created a new window. How the hell was my forms' name getting broken?

Well, I checked every damn thing I could see, and played around with the code for a quarter of an hour or so, before getting to that 'grasping at straws' stage when, having eliminated the impossible, whatever remains (however improbable) must be the cause. The only difference between the 'problem' form and the ones that worked was that it had a different border style. The others are all the usual 'Sizable' type, but this one is 'FixedToolWindow', because it's a little helper-utility that exists as a single instance. And unbelievably, when I changed it to have a 'Sizable' border, the Form.Name came back as 'frmXXX'.

So for some arcane reason (and there must be a reason, surely?) the WinForms engine chops the 'frm' prefix off the form name if it's got a 'FixedToolWindow' border style. I haven't tried this with other prefixes, because I need to press on - but if anyone out there knows what the cause of this behavior is, I'd be very interested to know. And yes, I know the use of Hungarian Notation for objects is now frowned-upon and I shouldn't be using 'frm' anyway - but until the Visual Studio Solution Explorer has a way of grouping objects so that my forms and other stuff are separated-out from each other and I can readily see which things are forms, and which are standard classes, I'm going to keep on defying convention.

Anyway, another few days should have the IDE running with a Disassembler view, so I'll post a screenshot or two at that point. Stay tuned!

EDIT: Just after I posted this, I re-ran the code and it looks like the naming glitch might actually have been caused by something else; when I switched the problem form back to 'FixedToolWindow', it still reports it's name as 'frmXXX' - so something got broken deep in the form engine system somewhere, and changing the border style fixed it. It's still wierd, but evidently not a fundamental problem. Hey ho.

Friday, 12 December 2008

Well, That Was Easy

Cracked-on with the project last night, tackling those tasks in my list from the previous post. First was to re-visit every line of code in the instruction decode/execution logic, to make sure it all looked good and did the right things in the best way. There were a few places where I could tweak stuff - mostly things like removing bit-masking code on 16-bit registers (either because the contents were being moved to an 8-bit register, or because I could replace the 16-bit register with an 8-bit one altogether) and occasionally re-sequencing things to eliminate temporary variables. Overall, it was in pretty good shape, and is now about as good as I can make it in its' current incarnation (i.e. as a basic select..case switchblock). Version 2 of the core, still some way off, will do this whole thing a very different way, but for now it suffices.

Second on the list was to convert the core classes into a DLL that the monitor would talk to, instead of having the actual classes included as part of the monitor project. In the past, before C#, .NET, and Visual Studio, this would have been quite a sizable task. It was certainly something I was not looking forward to doing; but once I knuckled-down and got stuck into it, it turned out to be a walk in the park. Add a new Project to the monitor Solution, choose Class Library (the new name for DLL), move all the core-related classes over to it, set the dependancy in the monitor project to use the Class Library, and build it. Seriously, a task I thought was going to take two or three hours ended-up taking about 30 minutes, including a couple of tests beforehand to see how it was going to work out.

In doing that, I got the third item on my list for free. Making the monitor use the DLL instead of included class files was all wrapped-up in making the DLL itself - by the time I had the DLL built, and almost before I realised it, the monitor project was using it. Again, something I thought was going to be an hours' work was actually done without even really trying. So I have to shout out a big Thank You to Microsoft for making this an almost trivial task!

So we now turn to item four, which is to create a GUI replica of the commandline monitor. So far, I have an MDI window and some menus defined, because I got that far and then decided to call it a night and dive into World of Warcraft for a while. But it's a start, and considering I wasn't expecting to start it for a few days yet, I'm a happy bunny.

Now you might be thinking that it's a good time for me to check all the existing emulator code into Google Code, but I'm not going to just yet, and here's why: although it all works well as far as it goes, it's still a country mile away from being ready for release (even just alpha preview release). The instruction support is at less than 50% of the official opcode set, because as you know I've just been implementing instructions as the VIC-20 ROM throws them at me; that means that not every instruction has been encountered, and for those that have we've only seen a subset of their addressing modes. Equally, none of the undocumented opcodes have been seen yet, and few of the systemic 6502 glitches are wired-in either (like the JMP page bug). So I'm not content to have World+Dog looking at it yet, because it's just not good enough in my eyes.

Instead, content yourself with a VERY early alpha screenshot of the IDE - so early, in fact, that it's almost mind-numbing in its' dullness. But at least you can see progress. :)

Thursday, 11 December 2008

Achievement Unlocked

A milestone was achieved as I worked on the code last night - there came a point when I entered 'g' (as in 'go', the command to start the CPU) into the monitor, and nothing happened.

Now this might sound like a disaster, but it actually means that the emulator is at the stage where it allows the VIC-20 ROM to get as far as the IRQ-dependant stuff. In other words, the ROM is running in a loop waiting for an interrupt to stuff something into the keyboard buffer, and therefore the core did not spit an 'Unable to decode the following opcode byte' message at me, which is what it does when the ROM gives it an unimplemented instruction. So, nothing happened. :)

If I were writing an actual VIC-20 emulator, this is the point at which I'd starting thinking about coding some logic to represent the 6522 VIA chips which generate timed interrupts and suchlike, in order to progress the overall machine. However, I've never intended to write such an application - loyal readers will know that I was only ever using the VIC-20 ROM as a test to get the emulated core off the ground. So with that done, we're fast approaching the point at which I'll be jumping-off into deeper waters, essentially configuring my own ROM to test instructions that have no code written for them yet.

But before I do that, I have a few other tasks to do:

1. Revisit the instruction decode logic to make sure everything is neat, tidy and efficient.
2. Repackage the code as a DLL so I can disconnect it from the commandline monitor.
3. Rework the commandline monitor to use the DLL version of the core.
4. Write a basic (but extensible) GUI replica of the commandline monitor.

Once we get to item 4, I'll have an IDE that talks to the core DLL and gives me the basis of what this project is all about - a fancy development environment in which I can configure 6502-based systems as I desire (using either pre-canned 'maps' like the VIC-20 one I have now, or creating all-new maps for sample 'machines' that don't exist in reality) and write code for them, and that has all the bells and whistles I want such as in-place instruction timing as you write the code. And the main reason for repackaging the code as a DLL instead of just embedding the classes in the host application is so that other environments (e.g. emulators) can plug it in if they wish...

For a chuckle, I ran a 'release' build version outside the Visual Studio environment to see what clockspeed I'm getting these days. You might recall I had some early success in optimising the logic and saw speeds around the 40MHz mark. Then things took a bit of a nosedive whilst I was doing some major reworking, and those speeds tumbled to around 50% of their best - no better than 18-20MHz on average. I've done a lot of work since then, and refactored a fair amount of the Register class logic, to name but one significant change.

Today, on a slightly creaky Dual-Core Pentium box, I see 42MHz. On the altogether sexier Core2Duo rig I do all my development and gaming on, the code yields 72MHz. That'll do. ;)

Wednesday, 10 December 2008

Nothing Compares 2 U

Progress, of a sort. Flushed with success after cracking the intricacies of the ADC and SBC instructions, I decided to conduct a small experiment - using my Sharp6502 monitor alongside the VICE monitor to single-step through the VIC-20 startup sequence, and see how well they matched-up. Obviously, I'd be looking for a perfect score here, as any differences would indicate something wrong with my code - and although VICE isn't a perfect emulation (there are some very specific VIC-related issues it doesn't handle) it's generally accepted to be pretty reliable when it comes to 6502 instruction execution, which means that if my CPU core does something different, it's almost certainly my code that's wrong and not VICE. With all my 6502-based machines in storage at the moment, I can't do a 'real' hardware comparison, so VICE is a good second-best.

So, on Sunday evening I got both VICE and Sharp6502 sitting alongside each other on-screen, and started them both at the cold-start vector ($FD22) in the VIC-20 ROM. And we got precisely eight instructions into the code before I noticed something - the 'CMP Absolute,X' opcode at $FD44 set the Negative flag (also known as the 'sign' flag) differently in my code. VICE said N was set, and Sharp6502 said it was clear. The other two flags affected by CMP (Carry and Zero) were in agreement, but N was most definitely not. So I stopped the test, and had a look at the CMP implementation, which was one of the first I'd done ages ago - if you remember, I was implementing instructions as the ROM presented them, so this was a bit of code that dated back almost to the first version of the CPU core.

Well, I looked long and hard at that code, and couldn't for the life of me see how it was going wrong. In fact, I even started to doubt VICE at one point, because the preceding instruction is an LDA whose operand is #$CD, which of course sets the N flag, and I began to wonder if VICE was somehow forgetting to reset N when it did the CMP afterwards. I pulled-up all the documents I could find that talk about the CMP instruction, and it looked like I was doing exactly the right thing. An example of what various sources say about CMP is:
 CMP - Compare [Z,C,N = A-M]
This instruction compares the contents of the accumulator with another value
and sets the zero and carry flags as appropriate. Processor Status after use:

C Carry Flag Set if A >= M
Z Zero Flag Set if A = M
I Interrupt Disable Not affected
D Decimal Mode Flag Not affected
B Break Command Not affected
V Overflow Flag Not affected
N Negative Flag Set if bit 7 of the result is set
Seems pretty straightforward, right? What could possibly go wrong with that? Well, after 36 hours of painful research, experimentation, and banging of the head on the desk, I can tell you exactly what can go wrong with it.

Y'see, it's like this. CMP is actually a pretty powerful instruction, because it does both magnitude and equality tests in one go; that is, on the basis of a single CMP you can tell whether the Accumulator contains a value the same as, bigger, or smaller than the value you're comparing it with. That's pretty clever, and is also where the devious little 'gotcha' lives that I'd not been cognisant of before this. The equality test sets (or clears) the Z flag, so that if the difference between the Accumulator and the test value is zero (i.e. they are equal) the flag is on. The 'bigger than' test sets the Carry flag, by just comparing the two values and setting the flag if the Accumulator is larger (or equal to, but that distinction is clarified by the Z flag).

But the N flag, which is the 'smaller than' test result indicator, is a tricksy little fiend that depends upon the natural ability of an 8-bit register to 'wrap around' when the value drops below zero. The key to this is recognising that little annotation in the instruction description for what it is, and what it means: [Z,C,N = A-M] tells us that the flags are set as a consequence of subtracting the memory (test) value from the Accumulator. And THAT means that although the Accumulator is left unchanged by CMP, it is actually doing an unsigned subtraction in the ALU to determine the result, and that means that subtracting a value of 2 from a 1 in the Accumulator, for example, gives us a result of 254 and not -1.

My result logic used an ordinary INT variable to do the calculation, which (using the 1-2 example just now) gave me -1 as the result, and of course Bit 7 wasn't set. Using a temporary 8-bit register as the result field, however, means that the wrap-around occurs and I get 254 as the answer, and Bit 7 IS set. Here's the code for 'CMP Absolute,X' - all the other addressing modes, as well as CPX and CPY, follow the same pattern:
 case 221: // CMP Absolute,X
_tempResult = _mem[_mem[-++_PC.Contents] + _XR.Contents];
_TR8.Contents = _AC.Contents - _tempResult;
_SR[_BIT0_SR_CARRY] = _AC.Contents >= _tempResult ? 1 : 0;
_SR[_BIT1_SR_ZERO] = _TR8.Contents == 0 ? 1 : 0;
_SR[_BIT7_SR_NEGATIVE] = _TR8[_BIT7_SR_NEGATIVE];
_PC.Contents++;
break;
We put the test value into _tempResult because it's used twice, and I don't want to hit MemoryMap more than once. You can see that we set Carry by doing a simple magnitude test between the Accumulator and the test value. Having subtracted the test value from the Accumulator and stored the result in a temporary 8-bit register (_TR8) we then check to see if the answer was zero, and set Z accordingly. Finally, we simply set N to whatever Bit 7 of the temporary register contains, as it will have wrapped-around if the result dropped below zero.

To close, here's a snippet of text cribbed from www.6502.orgs' tutorial on the CMP functionality of the CPU - it turned out to be the definitive and most helpful document I could find on this subject, and of course it's well-worth a mooch around the rest of the site for more information on all things 6502. Enjoy!


The N flag contains most significant bit of the of the subtraction result. This is only occasionally useful. However, it is NOT the signed comparison result, as is sometimes claimed, as the following examples illustrate:

After:

    LDA #$01 ;  1 (signed),   1 (unsigned)
CMP #$FF ; -1 (signed), 255 (unsigned)
A = $01, C = 0, N = 0 (the subtraction result is $01 - $FF = $02), and Z = 0. The comparison results are:
  • Equality comparison: false, since $01 <> $FF
  • Signed comparison: 1 >= -1
  • Unsigned comparison: 1 < 255
After:
    LDA #$7F ;  127 (signed), 127 (unsigned)
CMP #$80 ; -128 (signed), 128 (unsigned)
A = $7F, C = 0, N = 1 (the subtraction result is $7F - $80 = $FF), and Z = 0. The comparison results are:
  • Equality comparison: false, since $7F <> $80
  • Signed comparison: 127 >= -128
  • Unsigned comparison: 127 < 128
Notice that in both cases the signed comparison result is the same (the first number is greater than or equal to the second), but the N flag is different.

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?

Wednesday, 3 December 2008

Bitrot

"Bit rot, or bit decay, is a colloquial computing term used either to describe gradual decay of storage media or to facetiously describe the spontaneous degradation of a software program over time. The latter use of the term implies that software can literally wear out or rust like a physical tool."
I found an odd glitch when I got back into the code last weekend - everything was ticking-along nicely, until I entered the 'l' command into the monitor to dump the memory allocation table to screen. The software paused unexpectedly, and then Visual Studio kicked-in with a nasty 'Index out of range' exception and pointed me at the MemoryMap class. "What the hell...?", I muttered.

This code hasn't changed for a while now, and has been entirely stable since the last chunk of work I did on it. Moreover, I've hardly touched the project for three weeks, and it was working fine the last time I did anything with it. And anyway, the changes I'd just made for the Register classes went nowhere near the MemoryMap class. Some sort of weirdness was occurring.

Looking at the stack trace and other debug information, I could see that MemoryMap was being asked by the ShowMemoryAllocation routine to return a MemoryCell object for location 65536. This was obviously the cause of the 'Index out of range' exception, as the map is initialised as a 64K area with addresses in the range 0-65535, so anything outside of this (which should never occur!) is going to give the .NET runtime a headache. The big question was, therefore, how and why was location 65536 being requested?

Digging into ShowMemoryAllocation, I could see that this code, too, had not changed in some time. With no obvious place to focus, I restarted the monitor with a breakpoint set, and re-tried the command. As I single-stepped through the code, I hit this little bit of logic:
for (int i = 1; i < memSize; i++)
{
// Get the memory cells
MemoryMap.MemoryCell thisCell = _memory[(long)i];
MemoryMap.MemoryCell prevCell = _memory[(long)i + 1];
...
}
What this is doing is asking MemoryMap for the current memory cell (as we loop through the whole array) and also the previous one. This lets us see if the type or assigned purpose is different on the current cell, and thus spit out a summary message for the previous cell type. Now, can you see the error?

That's right - the reference to prevCell is being set as the MemoryCell at the current location PLUS one. So that would be the NEXT cell, not the PREVIOUS one - and when the current cell reference is location 65535, this asks for location 65536 and we blow the array upper bound. It's pretty obvious that prevCell should be asking for the cell at the current location MINUS one, and the loop this code is in starts at location one - that is, I designed this to handle the lower bound situation as well as the upper bound limit. So why the hell am I asking for the next location instead of the previous one?

I thought it must have just been a typo on my part, perhaps a moment of insanity as I was falling ill the last time I was in the codebase. But looking back through the Subversion logs, I saw that this line of code had not changed since I wrote it - and the initial version DID correctly look at the previous location. There was no update that changed the sign of the operation to a positive!

So, somehow, in the three weeks that the code lay dormant and I coughed and sneezed myself half to death, bitrot started to set in and that minus sign decayed to a plus. A single bit decays and flips the one next to it, and we have ASCII code 43 instead of 45; and ShowMemoryAllocation suddenly starts doing something dumb.

It's working now, of course - with the sign changed back to a minus, the whole routine bursts into life and does what it's supposed to. C'est la vie...

Monday, 1 December 2008

Class Action

Whew! HOW long since the last update?? Almost a month, that's how long. And after such an interval, you'll probably be assuming that all sorts of exciting things have happened to the emulator, right?

Wrong. The real world has kind of got in the way of this project over the last three weeks - the first two of which I spent mostly in bed feeling pretty sorry for myself, suffering from a dose of genuine the-one-that-can-kill-you Influenza. Aside from a couple of visits to the doctor for industrial-strength medication, I didn't stir from my bed for most of that fortnight. And then last week, getting back into the routine of the day job left me largely energy-depleted and nowhere near sharp enough to do any work on the codebase.

However, this weekend just past saw me feeling almost back to normal, so I opened-up Visual Studio on Saturday and had a look at where we were. I'm glad my commenting discipline is reasonably consistent, as it still took a good half-an-hour to re-sync my brain to the code and figure out where I'd got to, and what was next on the list of Stuff To Do.

Having optimised the bit-access logic for the registers, I could see that the classes that implement them (the Accumulator, X and Y, PSR, SP and PC) were suffering from a mild dose of chaos and needed a tidy-up. So as a way of easing myself back into harness, I took the existing classes and refactored them so that they were better representations of the objects they instantiate, and got the inheritance hierarchy nicely arranged.

So now, instead of separate specific classes for the 'special' register types (i.e. those with extended functionality above and beyond simple 8-bit register objects, like the PSR) we have a logical structure that implements a 16-bit register (Register16Bit, for PC), a derived 8-bit register (Register8Bit, that ignores the hi-byte from its' 16-bit base class, for A, X, Y and SP), and a further-derived 8-bit register (Register8BitStatus, derived from Register8Bit, for the PSR) that additionally always returns its contents with Bit 5 set to reflect the permanently-on state of that bit in the status register.

Naturally all the content manipulation accessors, bit indexers, etc. are implemented in Register16Bit, so the derived registers get that functionality for free. Register8Bit is an empty shell that just passes content access up to Register16Bit, but masks-off the upper byte. And Register8BitStatus is an even emptier shell, that defers to Register8Bit for everything except during queries on its contents, when it overrides the underlying value by ORing it with $20 to switch bit 5 on.

With everything neat and tidy again, I'm now back to the instruction implementations - and I'm currently grappling with the old foe, those twins of complexity that are the ADC and SBC instructions. I think I've got them working in normal mode now, and the next step is to confirm the Overflow flag behavior (which is horribly, horribly complicated) and then implement the Decimal Mode logic (which I've avoided thus far - it will either be a very simple task, or a nightmare to do efficiently).

Stay tuned!