SuperCPU Illuminated - Part 3
Well, so far we know how to recognize the SuperCPU and we also know that the 65816 has Native and Emulation modes. We've also learned how to use 16 bit registers. In this part of the tutorial, we'll show you some additional and more complex theories and excercises.
Modes, Modes and More Modes
The SuperCPU turns the C64 into a super multi-mode machine. Since there aren't just 1 MHz and 20 MHz modes for the SuperCPU, but also several diverse optimization modes, you can cut out the CPU, make only certian areas of memory available, and thusly speed up you programm run time. Check to see which mode the SuperCPU is in (Turbo or normal). Just so you don't forget these things, they are summarized again for you here: Table 1. All registers which must be described correspond to a specific word, are write sensitive, and already know what to do when they are set.
Other than these, the processor has two modes: Native and Emulation. To switch between them, there's a new flag: the E flag. In Native mode you can switch back and forth between 8 bit and 16 bit memory modes. For this we have the commands REP and SEP. The assembler must be divided then, according to bit width, because it needs to know how to compile the code. Here's and overview: Table 2.
In the last tutorial we always entered the command SEI before going into Native mode in order to prevent an interrupt while the processor was working. If an interrupt were to occur, the system would crash - but why? As we showed earlier, the jump vectors are located in a different place in memory in Native mode, including those for the IRQ. The interrupt routine was in ROM shortly before it had the opportunity to omit the favored vector at $0314/$0315. The actual IRQ vector ist at $FFFE/$FFFF, to which the processor always jumps when it gets and interrupt. The address $FF48, is a ROM routine where the register and its status are saved and then examined to see if it is an IRQ or BRK interrupt. Correspondingly, $0314/$0315 or $0316/$0317 will be branched out. In the Native mode of the 65816, however, all the vectors are positioned somewhat differently. The developers wanted to provide the option for the Emulation and Native modes to be able to write some interrupt routines. Table 3 shows how the vectors are positioned.
There's something new here too. As you can see, the vector for the IRQ is located at $FFEE/$FFEF in Native mode. The number combination $E5/$4C is located in ROM; this will jump you to $4CE5 with an IRQ. Do you always have to put your IRQ routines there? Of course not. It is possible, as was already stated, to protect against IRQ's during the running of a program in Native mode.
This is also true when you switch to Native mode from WITHIN an IRQ routine, and then when you switch back because no new IRQ's will be executed while the original one is being executed - and if new ones do come, they must wait until the original is done. This is the solution which you'd use most often when you want to enter demos or games in Native routines, most of which will work inside the IRQ routine. If you want to use Native mode in your main programs, however, the IRQ must be shut off. This isn't a problem most of the time, because at 20 MHz and 16 bits the time that the IRQ is shut off is not very long. But what if the IRQ is the next thing to be executed? In this case the ROM must be deactivated and a new IRQ routine must be written, and the vector at $FFEE/$FFEF must be in this routine. This raises a few questions: What happens, for example, if at the moment of an IRQ, 16 bit mode is activated, but the IRQ switches to 8 bit mode? These and other issues (how to put 16 bit words on a stack, for example) will be dealt with in another part of this tutorial.
There are now some new interrupts available: for example the "COP" interrupt. This interrupt exists to give control over to a COProcessor. There is a special COP command which is followed by a number. So if a coprocessor were available, you could communicate with it and give it a specific job to do. Nothing's been heard about such a coprocessor, though. So are the COP command and the vector belonging to it totally senseless? No, because why would a coprocessor have to be there to take care of special jobs? You can also use the COP command to call subroutines. A COP interrupt would be released, and the program counter would be saved from the stack. From it you would get the parameter, execute a routine, and switch back to the program with RTI (ReTurn from Interrupt)!
It's also striking that the Native mode has no reset vector. This means that the 65816 switches into 16 bit mode as soon as it recieves a reset signal, and then jumps to the corresponding vector. For the BRK command (the command which erases this interrupt) there is no vector in Emulation mode, because the of the existance of the Break flag. It is no longer possible to check the Break flag in Native mode, nor is it necessary, as there are some new vectors. ABORT deals with a better type of NMI - the processor can recieve this signal from outside, carries out the current command, and then branches out by way of the ABORT vector. The developers of cartridges like the Action Replay surely would have loved it if the 6510 had such an interrupt!
More About 16 Bits
In order to make the memory and Index registers in 8 and 16 bit totally independent from one another there are several diverse combination possibilities. It's interesting, at the very least, to work with commands like TAX, TAY, TXA, TYA, or TXS, TSX.
Just like the load commands (LDA, LDX, LDY), the transfer commands affect the N (Negative) and the Z (Zero) flags. (TXS has no affect on the flags, so you don't need to check up on it.)
But what happens when a TAY is executed if the memory is in 16 bit mode and the Index register size is 8 bit? The first rule about this, is that the type of transfer is always dependent on the destination register. In this case, only the lower 8 bits of the 16 bit memory will be transfered to the Y register. A second rule is as follows: as long as the Index registers are set to 8 bit, the Highbyte is always 0, until you switch back to 16 bit mode. Then the 8 bit contains the Lowbyte of the Index register, which it had before. Listing 3.1 shows what happens. In this example, the word which is at the address labeled DATA2 will be $0033 - only the lower 8 bits will be transferred, because the Highbyte is set to 0.
Memory works differently. When the memory's width is switched from 16 to 8 bits, the former Highbyte becomes "hidden" memory B. You can't get at this memory again without somehow changing the mode, and entering the command XBA (eXchange B and A). Listing 3.2 shows the behavior of the memory. After execution, the word $7F33 or $33, $7F in Lowbyte/Highbyte succession are located in memory at Result and Result+1. In other words: the Highbyte of memory, $7F, stays in 8 bit mode despite the mode switching.
If you try to transfer a 16 bit Index register to memory while it is being switched to 8 bit, Listing 3.3 will show you what happens. The result is $33FF, and it's clear that the inactive Highbyte of memory stays totally undisturbed - the Highbyte of the Y register will not be transferred. The rule that a transfer is always directed to the destination register is valid here as well - the hidden B memory is not changed.
If you transfer in the other direction, that is, from 8 bit to 16 bit registers, you must be clear about what happens. If you transfer the memory in 8 bit mode to an Index register which is in 16 bit mode, both 8 bit memories (i.e. the hidden memory as the Highbyte) will be transferred! The result, saved as Result in Listing 3.4 is $7FFF, and it's evident that not only the 8 bit memory A is transferred to the Lowbyte of the 16 bit Index register, but the hidden B memory is also transferred (and with it the Highbyte of the Index register). This means that we can generate a 16 bit index with the memory in 8 bit mode, and transfer the entire byte index, byte by byte, to the Index register without having to switch the memory to 16 bit mode first. But be careful: You shouldn't transfer an unknown Highbyte (B memory) to an Index register!
When transferring an 8 bit register to 16 bit memory, the contents of the Index register go to the Lowbyte in memory, while the Highbyte will be set to 0. This corresponds to the fact that when you switch the Index registers from 8 to 16 bit, the Highbyte gets set to 0. In Listing 3.5 a result of $0033 is yielded.
Now a few more words about the transfer of X to the stackpointer (the TXS command) and
the other direction as well (TSX). The stackpointer of the 65815 is 16 bit, which
means that the stack doesn't have to inevitably be located at $0100! It can be
anywhere within the 64K, and can of course be larger than 256 bytes. As a result of
this, the transfer correctly gets to its destination. If you transfer the 16 bit
stackpointer to an 8 bit index register, the lowbyte is now there.
It won't disturb anything else if you leave the stack at a default location of $0100. If you want to put the stack somewhere else or, for example, use it for an interrupt routine, you must create a new stack. This only works in Native mode, though! You just switch the Index registers to 16 bit mode, load the X register with a 16 bit word (the new address of the stackpointer should be presented, and then a TXS is executed), and the stack is located somewhere else. As usual, you should be aware of the processor mode. If you were to switch back to Emulation mode, the Highbyte would get set to $01 again!
So, to conclude, here's a little surprise: As well as the known transfer commands the 65816 offers the commands TXY and TYX, which you can use to directly transfer words back and forth between the Index registers, without having to transfer to memory!
The longer you use and play around with 16 bit mode, you will understand how much power there is in the SuperCPU. As an old 6502 miser you probably never would have thought about things like stack transfers from 16 to 8 bit. But now we know the background of the processor, which will be helpful not only when programming but also when debugging. The information from the second part of this tutorial has been strengthened and expanded on; play around and experiment with the power! Next time, we'll further explore some great abilities of the 65816 processor!
[Table 1]---> Complete V2 SuperCPU Memory Locations Register Function $D074 VIC Bank 2 Optimisation-Mode, only $8000-$C000 is mirrored $D075 VIC Bank 1 Optimisation-Mode, only $4000-$8000 is mirrored $D076 Screen RAM Optimisation-Mode, only $0400-$0800 is mirrored $D077 NO Optimisation, all memory is mirrored (default) $D07A switch to 1 MHz via software $D07B switch to 20 MHz via software $D07E fade in SuperCPU-Register in I/O region (for Optimisation) $D07F fade out SuperCPU-Register in I/O region (for Optimisation) $D0B8 Bit 6 =1: CPU is in 1MHz mode =0: CPU is in Turbo mode $D0BC Bit 7 =1: SuperCPU present =0: normal C64
[Table 2]turn Native mode on: CLC XCE turn Emulation mode on: SEC XCE 16 Bit memory: REP #%00100000 or REP #$20 8 Bit memory: SEP #%00100000 or SEP #$20 16 Bit Index register: REP #%00010000 or REP #$10 8 Bit Index register: REP #%00010000 or SEP #$10 16 Bit memory and Index register: REP #%00110000 or REP #$30 8 Bit memory and Index register: SEP #%00110000 or SEP #$30 Assembler should assemble 16 Bit A: £al (Memory Long) Assembler should assemble 16 Bit X/Y: £rl (Register Long) Assembler should assemble 8 Bit A: £as (Memory Short) Assembler should assemble 8 Bit X/Y: £rs (Register Short) Only for F8-Assblaster !
[Table 3]Vector Emulation Mode Native Mode IRQ $FFFE/$FFFF $FFEE/$FFEF RESET $FFFC/$FFFD - NMI $FFFA/$FFFB $FFEA/$FFEB ABORT $FFF8/$FFF9 $FFE8/$FFE9 BRK - $FFE6/$FFE7 COP $FFF4/$FFF5 $FFE4/$FFE5
© 1999 GO64 Redax & Count Zero/SCS*TRC for all HTML Stuff
[ To Part 2 ][ To Index ][ To Part 4 ]