Really enjoying this series, finally caught up after watching every video over past few days. Excellent example of proper test driven development, well done! One small thing, I think you have a bug in your addressing methods when calculating extra cycle if passing a page boundary (AddrAbsoluteX, AddrAbsoluteY, AddrIndirectY). You are not actually checking if you pass a page boundary - only checking if the value you are adding to the address is >= 255. Guessing your tests for this one aren't robust enough. Interestingly, you did this correctly in the conditional branch code - look at the BranchIf lambda. Great work so far - can't wait to see it run a full program
@DavePoo4 жыл бұрын
Thanks, I think you are right about that. Actually, i did also write the BranchIf incorrectly as well (but for a different reason and also pointed out in youtube comment), and that is fixed in a future video.
@cigmorfil41013 жыл бұрын
The instruction clock cycles are: BRK 1 - fetch OP, PC++ 2 - PC++ ; decode BRK, set B 3 - store PCH on stack; dec S 4 - store PCL on stack; dec S 5 - store P on stack; dec S 6 - fetch IRQL* to PCL; set I 7 - fetch IRQH* to PCH During an interrupt cycle 1 finishes previous OP, cycle 2 forces a BRK instruction, and the PC value is held. RTI 1 - fetch OP 2 - decode RTI 3 - inc S 4 - Fetch P; inc S 5 - fetch PCL; inc S 6 - fetch PCH Cycle 3 is needed as stack pointer points to next location to use and the 6502 can't actually pre-increment the stack pointer: the address is (synchronously) put out at the start of the cycle and the increment happens during the cycle. Using synchronous memory access the 6502 is split into 2 halves - every cycle something happens external (reading/writing memory) and internal (processing some data and changing its internal state). It does mean the 6502 often reads data which it then ignores, but it effectively gives it a 1 byte pipeline/cache which means the processor overlaps finishing one instruction whilst loading the pipeline/cache with the next instruction from memory. Whilst it decodes the instruction the processor has already loaded the next byte which can be immediate data or the low byte of an address (or the next instruction), ready for use. Being little-endian it can process the ADL (eg add an index) whilst loading the ADH. * this is what should happen, however due to a "feature" should a NMI occur at the same time as a BRK instruction it will load from NMIL and NMIH instead.
@andrsam36824 жыл бұрын
Hey Dave, thanks for this series. Great example of TDD approach.
@DavePoo4 жыл бұрын
Thanks, this CPU emulator seemed to be a perfect candidate for testing, is it basically has 152 discrete things it does, which can be tested on their own. I fixed the cycle count you mentioned in #34 (which isn't out yet)
@andrsam36824 жыл бұрын
@@DavePoo you inspired me with your project:) I'm currently working on Z80 emulator with option of emulating Z80's soviet clone Т34ВМ1, which didn't have some of the undocumented commands (obviously). And now I want to try TDD methodology, Unity framework, to be more specific (I'm programming in plain C).
@DavePoo4 жыл бұрын
@@andrsam3682 Go for it. I think one of my conclusions has been that the Unit testing approach is a very big help but you will need additional testing as well as it can't find everything. It's still possible to screw somethings up, as you pointed out about the branch cycles error, but once if you have a full suite of tests it's much eaiser to change the program and still be confident that you still have a working emulator at the end of it.
@_daniel.w4 жыл бұрын
This is actually really interesting, looking forward to watch the rest of the vid :)
@JulianOnions4 жыл бұрын
Enjoying this - one suggestion, make the instructions part of an enum, just for my OCD :) That way they are all part of something common, and you might even win on the switch statement detecting unused cases.
@DavePoo4 жыл бұрын
I could make them an enum, but I never deemed it a good idea because a) you are allowed duplicate definitions in an enum so it doesn't stop that b) FetchByte returns an byte not an instrucion, so an enum class would require an extra cast c) If there are unused cases then they won't have any effect on the program, the unit tests and test programs (in later video) will detect any cases that haven't been handled correctly d) every time i use the instruction in the unit tests, i am pushing the byte into memory to create the test progam, an enum class would require me to write 100's of casts, a normal enum would be better but then i lose type safety of knowing it's a byte (they also pollute the namespace) . So i just want the instructions to be what the progam treats them as "a Byte", if i make them an enum class they become a new type, if i make them a normal enum i can't define the Byte type explicity. Remember that the D in OCD stands for "Disorder" not "Order", so don't let that get in the way of reasoning about how a thing should work, something should exist or be a particular way for a reason, not because either your brain likes it that way, or it's a "rule of thumb". And if my code triggers your OCD, then please don't look at the VICE emulator, they don't even name the instruction, it's just a magic number in switch statement e.g. "case 0x61: /* ADC ($nn,X) */"
@JulianOnions4 жыл бұрын
@@DavePoo Yes fair enough, the casting would be a pain. I still like the grouping it gives you, but in the end I take my hat off to all your efforts!
@tv8g4 жыл бұрын
Hey so far iv'e learned a lot from your videos! You should create a discord chat, I think a lot of people would like if you had one.
@sandervanderhorst98513 жыл бұрын
You should check for the number of instructions handled
@CommanderKlag2 жыл бұрын
Hello, hello, Where is the video that shows the final emulator actually doing real world work, Where's the "Hello World" program or other real-world program example (non build tests), other than your work note & concept debugging tests? I saw the google copyright. I must have overlooked the actual program example apps that use your work. You did so much personal work. This is hudge, but I'm lacking confidance in spending hours on it as I would need to port it to Linux. Plus I was hoping to be able to execute programs, but I guess this is perfect for a windows developer. I would haft to spend hours to even determine if I can use it for my needs. It's allot of code. I'm sure it functions, but does it actualy work and do anything? That's the video I can't find. The final Presentation Video.
@DavePoo2 жыл бұрын
There is no actual final "Hello World" program for this, I just wrote a CPU emulator. To get a computer to display "Hello World" that requires video output capability which implies writing a full computer emulator. I didn't set out to do that in this video, as 35 videos was enough just getting the CPU emulator to go. I in the end I did get it to execute a test program from start to end, but the program itself does not produce any output, it just runs through all the functions of the CPU. If you are looking for a full computer emulation, you could look through the source code for VICE which emulates the C64 and other 8-bit platforms.
@rob8763 жыл бұрын
How do you do multiplication on the 6502?
@DavePoo3 жыл бұрын
Well i suppose short answer is "you express your multiplication as a series of additions", which is what multiplication really is mathematically. But you also have "shift left" which multiplies by 2, so you can use that to multiply by 2, 4, 8 etc. This webpage is worth a read if you are interested nparker.llx.com/a2/mult.html Also worth noting is that when you go up to 16-bit processors like the 68000 which could multiply natively. Check out how many clock cycles it takes to do the multiply, 70 clock cycles!!! wiki.neogeodev.org/index.php?title=68k_instructions_timings So you can see that there is no free lunch there for the CPU that could natively multiply, you might be able to write a faster multiply yourself if you knew something about the input data.
@cigmorfil41013 жыл бұрын
By addition: add together the values of one number multiplied by 2^(every bit set in second number). eg to multiply a number by 10 (decimal): 10 in binary is 0000 1010 so add together the number×2^1 and the number×2^3 since bits 1 and 3 are set: 9 × 10 = 9×2^1 + 9×2^3 = 18 + 72 = 90 By using right shifts the set bits of the multiplier can be detected, and by using left shifts on the number that number multiplied by the powers of 2 are calculated. The loop can either be done for a count of the bits in the multiplier, or until the multiplier equals zero. When multiplying two 8 bit numbers the result can be 16 bits, so 16 bits need to be used as the working storage for the result and number (whilst the multiplier uses 8 bits) during the loop.
@cigmorfil41013 жыл бұрын
@@DavePoo The time on the 68k is it doing the equivalent of the 6502 discreet instructions in micro code?