3:23 Increasing/decreasing the index registers/memory just wrap without any Carry. Carry is intended for Arithmetic operations, including the unsigned multiply/divide by 2 (the shift instructions), where the C flag is used to transfer between the bytes of a multi-byte number. Increasing/decreasing an index register/memory location is not considered an arithmetic operation hence they do not affect the C flag. The N flag (bit 7 of the status register) is always set to match bit 7 of any result (calculated, eg INC, ADC, ROL, or loaded: LDA, etc). Similarly Z is always set depending upon a similar zero result. An example is the charget routine of the PET (and C64) which has been located in zero page. It increases the low byte of the next character of the pointer into the program and if this is not zero it bypasses increasing the high byte. This code takes 6 bytes so by replacing those 6 bytes with a call to a subroutine (which includes increasing the pointer) you can intercept the charget routine and interpret your own commands, and intercept standard commands and use your own version of them (possibly how your C64 assembler is working).
@AxeMurderer22223 жыл бұрын
19:40 Most CPUs have some registers you are not implementing. MAR, MBR, & IR aka Memory Address Register, Memory Buffer Register, and Instruction Register. I think some of the confusion you are having with the clock counting has to do with moving stuff between the registers, the instruction decoding, and the MAR/MBR loading done behind the scenes, none of which are simulated by your code. When accessing memory the MAR is used to hold the memory address being accessed, and the MBR holds the data value that is read from or written to the memory bus. The IR is used to hold the instruction retrieved during the last fetch operation. The decoding operates there. You've simplified all that circuitry into a giant switch statement. All that addressing mode code you have going would be applied to values manipulated while in the MAR so that when it comes time to read or write, it is already in there ready to go out onto the bus with the correct values (based on the addressing mode selected in the instruction), since the MAR and MBR are connected to the memory and data busses. I suspect one of these registers (most likely the MBR) is where the data being operated on resides when you do something like a DEC. So the sequence is: read from mem, which loads the MAR from the instruction's opcode/operands and the MBR from the memory read access, then decrement the MBR (since the value is now already in there), then since the destination is already set in the MAR, and now the new value is already in the MBR, the only thing left is to tell the memory chip to write. No wasted clocks moving it into the accumulator to change it then back into the MBR again to write it. The MAR and MBR aren't needed during instructions that have no memory requirements. The MAR and MBR are both always set up with valid values before every memory access. So any garbage you put in them when they aren't needed will never interfere with memory operations. Therefore, it makes sense that they might be leveraged internally as general purpose registers in those situations where the currently executing instruction requires no memory accesses to occur, or after it has already finished with the memory.
@DavePoo3 жыл бұрын
Yeah. I think that's probably why it was a bad idea to try and do the clock counting the way i did. As i wasn't going to implement the microcode, so the clock counting sometimes doesn't make much sense without it, and the way some of the opcodes work.
@jorenheit3 жыл бұрын
34:25 You could templatize the LoadPrg function to deduce the array size for you: template Word LoadPrg(Byte (&program)[NumElements], ...) { /* not checked */ } Saves you from having to pass the number of elements :-)