[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: 65816 Loop Question
- Subject: Re: 65816 Loop Question
- From: dempson@actrix.gen.nz (David Empson)
- Date: Wed, 10 Apr 2002 13:07:07 +1200
- Newsgroups: comp.sys.apple2
- Organization: Empsoft
- References: <xnJs8.289204$2q2.26680870@bin4.nnrp.aus1.giganews.com>
- User-agent: MacSOUP/2.4.2
- Xref: archiver1.google.com comp.sys.apple2:18742
Bryan Parkoff <BParkoff@satx.rr.com> 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