Bryan Parkoff wrote: > 65816 Loop Question > > I understand that 6502 and/or 65C02 can only limit up to 64KB. The loop > can only cycle from $0000 through $FFFF. If it tries to cycle from $FF00 > through $1000F, it will only cycle from $FF00 through $000F. That isn't even a valid concept on the 6502/65C02. The CPU's address bus is 16 bits, which means it is only able to directly address memory locations in the range $0000 to $FFFF (64 KB). Bank switching operates by replacing portions of the 64 KB address space, but as you say, that is a different matter. > Do 65816 have access to cycle the loop beyond 64KB without needing to > use bank address switching? It depends on the CPU mode settings and the addressing mode of the instruction used. The CPU can be operating in emulation or native mode. In emulation mode, it behaves like a 65C02 with some extra registers and instructions, and somewhat limited ability to use memory beyond 64 KB (e.g. you cannot execute code outside bank 0 unless interrupts are disabled, but accessing data is no problem). The X and Y index registers are stuck at 8 bits in emulation mode. In native mode, the full address space of the CPU (16 MB) can be used safely for code, and the X and Y index registers can be set to either 8 or 16 bits. The relevant addressing modes are as follows, with examples in each case. LDA $nn,X Direct Indexed LDA $nnnn,X Absolute Indexed LDA $nnnnnn,X Absolute Long Indexed LDA ($nn),Y Direct Indirect Indexed (post-indexed indirect) LDA ($nn,X) Direct Indexed Indirect (pre-indexed indirect) LDA [$nn],Y Direct Indirect Long Indexed (post-indexed indirect long) The "Absolute long indexed" and "Direct indirect long indexed" modes only exist on the 65816. All "direct" modes are known as "zero page" modes on the 6502/65C02. On the 65816, the 16-bit D register is used to set the base address of the direct page, so it can start anywhere within the first 64 KB of the 16 MB address space. This means that the D register is added at some point in the calculation of the effective address. If the D register is set to zero, then the direct modes behave just like the zero page modes of the 65C02. All of the plain "indexed" modes (Direct Indexed, Absolute Indexed or Absolute Long Indexed) operate by adding an index register to the specified base address to determine the address to be accessed. The Direct Indirect Indexed mode operates by fetching the contents of two consecutive direct page locations, treating them as a 16-bit pointer, then adding the Y index register to form an absolute address which is to be accessed. The Direct Indexed Indirect mode operates by adding the X index register to the specified base address, fetching two consecutive direct page locations from that address, and using them as an absolute address which is to be accessed. The Direct Indirect Long Indexed mode operates by fetching the contents of three consecutive direct page locations, treating them as a 24-bit pointer, then adding the Y index register to form an absolute long address which is to be accessed. For all 'Direct' modes, the D register is added just before the contents of the direct page memory location is accessed, i.e. after adding the index register to the base address for 'Direct Indexed' and 'Direct Indexed Indirect' modes, or before fetching the pointer for 'Direct Indirect Indexed' and 'Direct Indirect Long Indexed' modes. Indexing on direct page addresses are limited in two ways: - The effective address of the direct page location is always limited to the first 64 KB. - If the CPU is running in emulation mode, the calculation of the direct page address is limited to 8 bits, i.e. only the 256 bytes starting at the D register can be accessed. (If the D register is $FF01 or higher, the address calculation will also wrap around to $0000.) If the CPU is running in native mode, direct page indexing can potentially access the first 64 KB. Indexing on absolute addresses (including the Direct Indirect Indexed mode) uses the data bank register (B) to determine which bank is to be accessed. This register is normally set to zero while in emulation mode, but this isn't required. In emulation mode, the absolute address calculation is limited to 16 bits, so the address will wrap around within a 64 KB bank. In native mode, the absolute address calculation can cross the bank boundary. Indexing on absolute long addresses (including the Direct Indirect Long Indexed mode) is always able to cross a bank boundary. In summary: - If the CPU is running in emulation mode and you use 6502/65C02 addressing modes, then you can only access 64 KB, just like the 6502/65C02. - If the CPU is running in native mode, you can cross the bank boundary for 'absolute indexed' and 'direct indirect indexed' modes. Since X/Y can be set to 16 bits, this allows you to access up to 64 KB of data starting at a specified 24-bit base address, which must start within the current data bank. - If you use 65816 "long" addressing modes in either emulation or native mode, you can access the entire 16 MB address space and don't need to worry about bank boundaries. You can only access 64 KB from a given base address, because the index registers are limited to 16 bits, but you can modify the base address as necessary. Code execution is always limited to the current 64 KB bank. The only way to change banks is with a JML (long jump), JSL (long call) or RTL (long return). Interrupts and RTI can also switch banks in native mode. > I would love to see that the cycle will execute from $00/F000 through > $02/A000. That particular example would be rather unwise, because bank 1 is the equivalent of IIe's auxiliary memory, so it has I/O locations in $01/C000-CFFF and has special rules for using $01/0000-$01/07FF and $01/D000-$01/FFFF (as well as some parts of the RAM in the $01/0800-$01/BFFF area). It is also worth noting that you must use the IIgs Memory Manager toolset if you want to make use of any RAM outside bank 0 and 1, so that you don't step on RAM being used by other software. In general, your application should not assume it can allocate specific memory addresses in banks 2 and higher - you should ask the memory manager for the amoount of memory you require without specifying the address, and then do any appropriate address calculations in software. If you are writing a native application (running under ProDOS-16 or GS/OS), then you also have to use the Memory Manager for banks 0 and 1. Assume you have requested a memory block which is $01B000 bytes in length, and the Memory Manager happens to allocate memory starting at $02F000 (through to $04AFFF), you would typically set up three direct page locations to hold the base address ($02F000) and use the direct indirect long indexed mode to access the memory. In this case, you would be able to access $02F000 through $03EFFF by simply incrementing the Y register from $0000 through to $FFFF. I you wanted to access memory beyond this point, you would have to modify the pointer by adding one to its bank byte, then continuing with Y register values from $0000 through to $AFFF. e.g. the following code will clear a block of memory which is larger than 64 KB. (This is the first Apple II code I've written for several years, so I might be a little rusty.) ClearMyHand: ; Need initial code to set up for using the Memory Manager, and passing ; parameters to the _NewHandle call in order to allocate memory. ; Assume this call has returned a handle, which has been stored in ; a 4-byte direct page variable called MyHand. In addition, the amount ; of memory allocated has been stored in MyHandSize. ; The CPU is currently in native mode, with A and X/Y all set to 16-bit ; mode. ; Lock the handle so that the memory won't move lda MyHand+2 pha lda MyHand pha _HLock ; Dereference the handle to get a pointer to its base address ldy #$0002 lda [MyHand],y sta MyPtr+2 lda [MyHand] sta MyPtr ; Set up the X register with the number of banks we need to clear ldx MyHandSize+2 beq BanksDone ; Set up the Y register as the index into the bank, and A with the ; value to be stored. ldy #$0000 tya ; Loop to clear each bank. A is 16 bits, so Y is incremented by 2 ; after each store. BankLoop: sta [MyPtr],y iny iny bne BankLoop ; Update the pointer for the next bank, and keep going if we have ; more banks to follow. inc MyPtr+2 dex bne BankLoop ; We've finished processing complete banks. Now do the last partial ; bank, checking specially for an odd number of bytes. BanksDone: lda MyHandSize lsr ; A has number of words to clear beq NoWords php ; Save the carry flag for later tax ; Set X to the number of words to clear lda #$0000 ; Reinstate the zero value being stored WordLoop: sta [MyPtr],y iny iny dex bne WordLoop plp ; If carry is set, we have an odd number of bytes, so we have to ; set the accumulator to 8-bit mode for the last byte. NoWords: bcc NoOddByte lda #$0000 ; Reinstate the zero value being stored sep #$40 ; Set the M flag (8 bit accumulator) sta [MyPtr],y rep #$40 ; Clear the M flag (16 bit accumulator) NoOddByte: ; Unlock the handle again lda MyHand+2 pha lda MyHand pha _HUnlock ; Done! rts -- David Empson dempson@actrix.gen.nz Bryan Parkoff wrote: > Thank you for the answer. It does make sense that I understood > completely. > > You use two words -- Direct and Indirect. Indirect is like pointer. Correct, but it looks like you didn't understand "Direct". "Direct" in this context refers specifically to the "direct page", i.e. the 256 bytes starting at the location pointed to by the D register (which is always within the first 64 KB). There are other addressing modes which are also only able to access memory within the first 64 KB. I think that "Direct" page was a poor choice for its name, because of the resulting confusing names for addressing modes. It was originally called "Zero" page on the 6502 and 65C02, where the "zero" implies that it starts at location zero. The only thing that changed is that the start address can be set anywhere within the first 64 KB. It might have been cleared if they had called it something like the "Short" page or "Frame" page. Native 65816 applications often use the D register like a frame pointer. > If we say -- Direct Direct, it can mean to access inside 64KB. No. That isn't a valid concept - there is no "Direct Direct" addressing mode. Any access to the direct page (or indexed from an address within the direct page) can only fall within the first 64 KB. An absolute address might also fall within the first 64 KB (if the data bank register is set to zero). > If we say -- Direct Indirect, it can mean to access inside and outside > 64KB. It looks neat. Not exactly. The addressing modes "Direct Indirect", "Direct Indirect Indexed" (post-indexed) and "Direct Indexed Indirect" (pre-indexed) all fetch a pointer from direct page and then access a memory location within the current data bank. In native mode, this can cross a bank boundary. The addressing mode "Direct Indirect Long Indexed" can access the entire 16 MB address space. Here is the full set of 65816 addressing modes: 1. Immediate #nn, #nnnn 2. Absolute aaaa 3. Absolute Long aaaaaa 4. Direct dd 5. Accumulator A 6. Implied 7. Direct Indirect Indexed (dd),Y 8. Direct Indirect Long Indexed [dd],Y 9. Direct Indexed Indirect (dd,X) 10. Direct Indexed with X dd,X 11. Direct Indexed with Y dd,Y 12. Absolute Indexed with X aaaa,X 13. Absolute Long Indexed with X aaaaaa,X 14. Absolute Indexed with Y aaaa,Y 15. Program Counter Relative looks like aaaa, +/- 128 offset 16. Program Counter Relative Long looks like aaaa, +/- 32K offset 17. Absolute Indirect (aaaa) 18. Direct Indirect (dd) 19. Direct Indirect Long [dd] 20. Absolute Indexed Indirect (aaaa,X) 21. Stack 22. Stack Relative dd,S 23. Stack Relative Indirect Indexed (dd,S),Y 24. Block Move I've already explained the various "Direct" modes use an 8-bit address. They can access 256 bytes starting at the D register possibly after adding an index register (limited to the first 64KB of the address space). The indirect variants end up fetching a pointer from somewhere within the first 64K, which is then treated as an absolute or absolute long address. The "Absolute" modes (not including the "Absolute Long" modes) use a 16-bit address. They can access any location within the current data bank. In native mode, then can index beyond the bank boundary. The indirect variants are only used with the JMP, JML and JSR instructions, and are used to look up tables of function pointers. The "Absolute Long" modes use a 24-bit address. They can access the entire address space. The "Stack Relative" modes work similarly to the "Direct" modes - they use an 8-bit offset from the stack pointer. Mode 22 (stack relative) is equivalent to mode 4 (direct) except that S is used as the base address instead of D. Similarly, mode 23 (stack relative indirect indexed) is equivalent to mode 7 (direct indirect indexed). Both of the stack relative modes are limited to the first 64 KB to fetch the data from the stack. The pointer fetched in mode 23 is treated as an absolute address, so the subsequent indexing with Y can cross a bank boundary. -- David Empson dempson@actrix.gen.nz