and btw, I'm really enjoying this ! As an Apple II lover and as a developer. I haven't play with C++ since 2010 so I'm pleased to discover new things. And I'm delighted to see you're a TDD follower ;)
@DavePoo4 жыл бұрын
I think TDD is great if you can make it work for your project. A CPU emulator is a really good candidate for that. I never had an Apple, back in the day, for 6502 computers, i had C64 and Atari 8-bit. I own a working C64C
@DavePoo4 жыл бұрын
Oh and the SNES, i think that was 6502 as well?
@Bahamutkotd Жыл бұрын
@@DavePoo 65816 the 16 bit version of the 6502, which booted in 6502 mode.
@OliverWieland3 жыл бұрын
Nice series of videos! FYI; the reset vector is not the location of the first command, but the location of the adress of the first command. That means: FFFC --> 0x00 FFFD --> 0x80 ==> After Reset, the PC will be set to 0x8000 and begins the execution there.
@DavePoo3 жыл бұрын
Yeah, i did get that wrong, but ultimately it wasn't too important in getting the emulator working. I would have got to that if i ever started emulating the ROM of the actual computer
@ColinRichardson8 ай бұрын
13:13 I am glad you said this, because in my head I remember doing some simple assembly years ago, and remembered the the stack was backwards.. But, I just assumed I was wrong..
@Handskemager3 жыл бұрын
Dude, love this series! Never had a 6502 so this is “new” to me. The suspense was killing me with that double 0xFF03 xD
@DavePoo3 жыл бұрын
38:17 - Whoops!
@christianlett3 жыл бұрын
The confusion about pushing PC-1 comes from when in the process the 6502 fetches the next instruction - in reality it increments the PC and fetches the next instruction into the Instruction Register (IR) at the *end of the current instruction*, rather than doing it at the start of the instruction. At the end of RTS, the address is one byte before it needs to be, as described in the documentation, but then the RTS instruction increments the PC, which puts it in the correct place for the next instruction.
@Kris_M3 жыл бұрын
Yes, PC is incremented at the end of an instruction.
@Lord-Sméagol3 жыл бұрын
The return address caught my attention when I first did some 6502 coding years ago (I grew up on Z80, which pushes the address of the next instruction). The way 6502 does it actually makes sense; a lot of the instructions fetch the next instruction while completing the current instruction to save time. "The Visual 6502" (www.visual6502.org - if YT will show this link) has an excellent transistor-level simulation (the Visual Sims link). You can see the pipelining at work by selecting and watching the trace log as you advance the clock 1/2 cycle at a time.
@DavePoo3 жыл бұрын
Yeah, some others have posted this since the I made the video. I suppose if i had seen the microcode documentation for this it would have made more sense. I think was coming from the point of view of "why would they do that when it seems really inconvenient", but actually it was really convenient for the hardware design and that's why it works that way.
@hjups3 жыл бұрын
Did you ever figure out why the indirect addressing modes took different cycles? If not, it has to do with the uncertainty of the address calculation, since the address calculation could only be done 8 bits at a time. This means that you can only guarantee that you have the correct address in the 5th cycle. The load instructions behave the same way, with the caveat that they are able to short circuit the state machine (and proceed to the next instruction one cycle early) if there was no carry. This can be done because the 6502 bus is either in a read or a write state (no idle). So by default, the bus is always reading since reads are not destructive. Writes on the other hand are destructive and thus can only be allowed once the true address is known. To simplify the hardware, the load instructions probably clobber the value in the register being loaded with the first read (valid or not), and then perform another read if the initial address was incorrect.
@tmbarral6644 жыл бұрын
@Dave Poo it's funny to watch you without beign able to talk to you to tell you where the prob is ;) The stack ;) decrement it before storing ;) Nice one :)
@DavePoo4 жыл бұрын
I know, sometimes i am shouting at the screen when watching my own videos. But these are not really tutorials, its just me writing a whole program.
@christianlett3 жыл бұрын
The microcode for RTS in my emulation has to waste three whole cycles to use up the six required. It's been a while, so I don't recall why the actual processor uses 6, but it may also waste cycles during an RTS.
@DavePoo3 жыл бұрын
Yeah, in-lining functions back in the day was a big win saving you 6 whole cycles on the return.
@djchrisi10 ай бұрын
There is a nice little trick in Visual Studio. If you put memory.Data+0xff,10 in the watch window it will display the array with an offset of 0xff with 10 items.
@tmbarral6644 жыл бұрын
Dave, for AddrAbsoluteX, you started initially with the ZP version, dealing with a single byte for the address. when we add the offset we need to check if there is a carry to increment the MSB. So here, it is the absolute mode, so we have a 16 bits address and to make the things easier, it's dealing always with the 2 address bytes, no matter the carry.
@DavePoo4 жыл бұрын
Is the question "do we need to check the carry when addressing"?
@tmbarral6644 жыл бұрын
@@DavePoo no, no, sorry. doh.. I misused the word carry here. I was referring to the overlapping value to the MSB, not the C flag.
@DavePoo4 жыл бұрын
Perhaps if you could restate the question then, as i'm not really sure what you are asking? Are you asking about absolute X addressing? The MSB of the absolute X addressing is correct when we do it in our emulator because we do it as a 16-bit (Word) addition. I think the actual processor might probably do it as an 8-bit addition to the LSB and then an another addition of 1 to the MSB if the page boundary was crossed.
@zicada76613 жыл бұрын
Couldn't addrabsolutex and y take a bool as to whether to add an extra cycle when passing the page boundary, instead of duplicating the methods like that
@Salsuero4 жыл бұрын
Watching you input bytes manually and seeing you write to FF03 twice, overwriting the LDA command... ouch! It's like a suspense thriller where you know the killer is on the other side of the door while the victim walks toward it any you're screaming at the screen... don't open the door! LOL
@DavePoo4 жыл бұрын
It's the Alfred Hitchcock method of programming.
@jamesExiledLegends3 жыл бұрын
spotted this too! Currently at 22:03 waiting to see if he'll spot it 😬
@gamemaster2618 Жыл бұрын
It got so intense I fell asleep faster in this video than the others … still watching to fall asleep hehe 😛
@tmbarral6644 жыл бұрын
Dave, why not having the cycles counter as a property of CPU ? less parms to pass to every function. And only used by execute to compare the expected count. This can also be applied on the mem parm. You can have a constructor for CPU with the mem parm. What are your thoughts on this ?
@DavePoo4 жыл бұрын
The cycle counter doesn't actually need to be stored, it just needs to be counted whilst executing. If you think about it this is how the clock (system clock signal) works in a computer. The clock just pulses on/off and is used to synch the system functions, it doesn't actually count a meaningful value. And then there is the "object oriented" problem if you stored it, which object owns the value? You say store it in the CPU, but then the memory also needs the clock, and other systems like the Video chip would also need the clock as well. So really a clock is not a value that any of those object should own, as if it did, they would all become intertwined and that would make unit testing much harder, i would have to create a CPU to test a memory write function. So yes you could store the cycle count as a property in the CPU and that would work, but once you finished executing some cycles, you would be left with a meaningless value stored that you don't need. Also, if you look at the pinout on an actual CPU you will see that it too has pins where it reads in the clock from an external source, just like the software is doing. It may be that the clock would need more thought if i designed an entire computer emulator, but for the purpose of getting a working CPU emulator what i have works. As for the memory not being a property, it the same thing, i want the memory to be something i could unit test without having to create a CPU to test it, less depenencies mean it's easier to test. I think memory would get more complex in a full computer emulator, as you have memory mapped I/O, roms etc to deal with and read/write functions that write to different things depending on the direction, so keeping it as discrete as possible would allow better testing i think (but again your mileage might vary, you could try your own approach)
@tmbarral6644 жыл бұрын
@@DavePoo ;) Cheers. My point was only about the fact you have to pass the Cycles parm to all function. that's ok. but not that elegant. that was what I was trying to say. And if you don't pass it, you can have the temporary value holder as an instance variable of the CPU class. ;)
@DavePoo4 жыл бұрын
You could do that, but you would be storing a temporary value. I'm not a big fan of that, I think that is less elegant, i think i would rather pass it around as i think it's easier to reason about. But there is no right answer here, your solution would work, it would just be different. There are always pros/cons to any solution to a problem and the real problem with most real world problems is that there is usually more than one right answer, so choosing one can be difficult.
@tmbarral6644 жыл бұрын
@@DavePoo you're right. let's say I'm always kinda foreseing what is the code in ASM ;) So I'm always wondering if there is a real purpose for any kind of parm. And my rule of thumb is to avoid them if not necessary as they tend to add noise to the choir. I mean the less to read, the easier to understand. That was I meant by elegant ;)
@DavePoo4 жыл бұрын
Well, more parameters add more noise, but then so do more member variables, except member variables are harder to reason about as you have examine every other member function to find out how they are used, parameters only exist inside the function so are easier to reason about. I think this is where there is a different between neat looking code and good code. Code can look neat but be more difficult to reason about. I would be careful of "rules of thumb", as you have to know why that "rule of thumb" exists before you can use it correctly, as you have to know when to ignore it.
@twobob4 жыл бұрын
Doubtless someone else mentioned it but your JumpsAndCallsTests failed also because you wrote 0x42 to 0xFF03 /not/ 0xFF04 (meaning it would try to execute opcode 42 rather than shift anything into the A register AIUI). eh I'm just adding this for the next OCD person who expected to see it as top comment.
@twobob4 жыл бұрын
38:23 finally, Agony. Oddly enjoyable to watch others stumble in the way we all have and do. Decent series.
@m1geo Жыл бұрын
Your jumps and calls test will pass if your JSR and RTS functions don't do anything... Your PC will continue and you'll eventually LDA to 42.
@1Eagler4 жыл бұрын
I haven’t used C++ for 30 plus years but It looks like C, not C++.
@DavePoo3 жыл бұрын
I didn't do any encapsulation or polymorphism in this so it's more C'ish than C++'ish.
@LunaticEdit11 ай бұрын
For anyone coming here later, there's no way this function will work correctly as written. You have to read the first operand byte, push the highword of PC, push low word of PC, then read the second operand byte. While what this guy has works 95% of the time, it's not 100% accurate and WILL fail Tom Harte's unit tests for some of the edge cases.
@robertgrant7213 жыл бұрын
Every time the cpu does something it requires at least a cycle!
@robertgrant7213 жыл бұрын
Cut my teeth programming an Atari 400 on the membrane keyboard! :)
@robertgrant7213 жыл бұрын
Stack should pre-decrement on push and post-increment on pop
@DavePoo3 жыл бұрын
The stack is wrong in this video (also i stored the pointer as 16-bit instead of 8-bit), it was corrected in a subsequent video.
@robertgrant7213 жыл бұрын
@@DavePoo No worries, I'm enjoying the walk down memory lane :) Just wish I was sitting at your side as you worked through this. Thanks for sharing your progress.
@DavePoo3 жыл бұрын
Thanks, i wish i had this many comments on the videos during the course of doing them. Would have got through these issues quicker and easier
@Salsuero4 жыл бұрын
Well... comments are often the weakest tool in a coder's arsenal... you end a comment with ) but you removed the ( rendering the ) entirely unnecessary.
@DavePoo4 жыл бұрын
The only problem with comments is "The programmer doesn't read them and the compiler doesn't read them"