The "Easy Banking" scheme is a memory-control design which allows the user to access up to 24K-30K of ROM and 6K of RAM (6K of ROM is copied to RAM on startup and is inaccessible thereafter) without any bank-switching instructions. There are a few restrictions on memory layout, but the memory access will be far smoother than with any other banking system.
The cartridge memory space is divided into sixteen 2K regions. Eight of these are called "code banks", and the other eight are "data banks". Code is only allowed to run in code banks, or in RIOT RAM from $0880-$08FF. The behavior of running code from any other address is unspecified. Data may be accessed from within the current running code bank, or from any data bank. The effect of accessing data from a code bank other than the current one, or accessing data from a code bank when running from RIOT RAM at $08xx, is unspecified.
One of the code banks will access the first 2K of RAM, which is on startup pre-loaded with ROM from $4000-$47FF; the other seven will access ROM from $0800-$3FFF. To jump from one bank to another, all one needs to to is use a JMP or JSR to the proper address, or do an RTS with the proper address on the stack. The only restriction is that target of a JMP or JSR instruction must not be a multiple of 2K away from either either last byte of the JMP or JSR instruction, nor the first byte of the next instruction, unless the target address is in the same bank as the JMP/JSR instruction itself. An RTS may be located anywhere except at the very last byte of a code area, and is allowed to return to any valid code location.
The first three data banks will access 6K of RAM, which is pre-loaded with ROM from $4000-$57FF. The remaining five data banks will access ROM from $5800 to $7FFF.
The RAM at $1000-$17FF, $3000-$37FF, and $5000-$57FF may be written using STA, STX, STY, or SAX instructions, using any supported addressing mode. The effect of other instructions is unspecified. Data written to RAM at $1000-$17FF will also appear 2K higher in the RAM code bank from $1800-$1FFF. Note that writing to addresses $1800-$1FFF is not supported; one must write to addresses $1000-$17FF.
**CODE ADDRESSES**
6507 Address ROM Address
1800-1FFF 4000-47FF+
3800-3FFF 0800-0FFF
5800-5FFF 1000-17FF
7800-7FFF 1800-1FFF
9800-9FFF 2000-27FF
B800-BFFF 2800-2FFF
D800-DFFF 3000-37FF
E800-FFFF 3800-3FFF
**DATA ADDRESSES**
6507 Address ROM Address
1000-17FF* 4000-47FF+
3000-37FF* 4800-4FFF+
5000-57FF* 5000-47FF+
7000-77FF 5800-4FFF
9000-97FF 6000-47FF
B000-B7FF 6800-4FFF
D000-D7FF 7000-47FF
F000-F7FF 7800-4FFF
(*) Address is writable
(+) Address is RAM which is initialized from ROM
NOTE: Not quite up to date with latest changes in StellaIn fetcher mode, the cartridge will dynamically generate 6502 code to implement a specialized instruction set. Every instruction will be 16 bits, and will take 2 to 4 cycles to execute. Instructions will be assigned 4-5 character mnemonics to distinguish them from 6507 opcodes. Opcodes ending with "+" may have an "L" or "J" suffix to specify that they should do "Loop-test" or "Jump-loop" (see below) in addition to their normal function.
The JX10 Fetcher itself keeps three pieces of state information, all of them addresses: the next JX10 instruction, the fetcher group 0 table, the and fetcher group 1 table. All other state is manged by performing read-modify-write operations on the data in the fetcher tables, or memory pointed at by such data; any of these latter changes will be directly visible to the target application.
REST+ | 3 cycles | JMP $1010
| This instruction takes 3 cycles to execute. A REST instruction must be done at least once every 4,000 cycles or the Atari may malfunction. It translates into JMP $1010.
| QUIT addr | (EXITS) | JMP addr
| Generates an "JMP a" instruction and returns to normal code execution.
| LIMA value LIMX value LIMY value | 2 cycles | LDA #value LDX #value LDY #value
| Load A, X, or Y with an 8-bit immediate value
| LDFA group,fetcher,inc LDFX group,fetcher,inc LDFY group,fetcher,inc | 2 cycles | LDA #data LDX #data LDY #data
| Load A, X, or Y from fetcher #f of group #g and increment it by i. (lda #imm, etc.)
| SDRA+ addr SDRX+ addr SDRY+ addr SDRQ+ addr | 3 cycles | STA addr STX addr STY addr SAX addr
| Store A, X, Y, or (A&X) to specified TIA address
| SSDA+ addr SSDX+ addr SSDY+ addr SSDQ+ addr | 4 cycles | STA.w addr STX.w addr STY.w addr SAX.w addr
| As above, but takes 4 cycles.
| SDFR group,fetcher,inc | 3 cycles | STA addr STX addr STY addr SAX addr
| Get a byte from fetcher #f or group #g, increment it by i, generate a STA, STX, STY, or SAX instruction. Use top two bits of fetched value to select instruction; remaining 6 bits for address.
| SSFR group,fetcher,inc | 4 cycles | STA.w addr STX.w addr STY.w addr SAX.w addr
| As above, but takes 4 cycles (sta abs, etc.)
| TSTS addr,g,f | 3 cycles | BIT addr
| Read specified address and store the result to RAM pointed at by the specified fetcher (single-increment mode only). Useful for collision detection.
| TSTI addr,group,fetcher | 3 cycles | BIT addr
| Read the specified address and increment the upper byte of the indicated fetcher if bit 7 is set, and/or the lower byte if bit 6 is set. Useful for paddles. |
|
The system has two fetcher-pointers. To perform a fetch-retrieval (e.g. for "LDFA") the system selects one of the pointers using the "g" bit. It then performs the following logic:
addr1 = ptr + f*2 addr2 = memw[addr1] dat = mem[addr2] generate opcode from dat if i<>max then addr2 += i else addr3 = memw[addr1+2] dat = mem[addr3], and update flags if dat was zero addr3++ else dat-- mem[addr3]=dat endif if addr3 is odd then addr2++ endif memw[addr1] = addr2
|
Loop-test and Jump-loop behavior:
dat = mem[fetchptr0-4] if dat=0 fetchptr0 = memw[fetchptr0 - 2] fetchptr1 = memw[fetchptr0-8] ' Use new value of fp0 virtpc = memw[fetchotr0-6] else dat-- memw[fetchptr0-4] = dat dat = mem[fetchptr1-4] if dat=0 fetchptr1 = memw[fetchptr1 - 2] else dat-- memw[fetchptr1-4] = dat endif if (is jump-loop instruction) virtpc = memw[fetchptr1-6] endif endif
|
JX10 Banking
Notes
- Hexadecimal constants are preceded by a dollarsign. Within a hex constant, "L" means any digit 0-7, "H" means any digit 8-F, "J" means any odd digit, "N" means any even digit, and "X" means any digit.
- Certain operations are explicitly described as "forbidden". The effect of forbidden operations may vary between different versions of the ARM code; it is recommended that emulation stop if code performs a forbidden operation.
- There is going to be a "super-fetcher" mode, details TBD, which behaves totally differently from what's described here; any emulator design must be able to switch between normal mode and super-fetcher mode.
Atari Programming
There are eight code banks, distinguished by bits 13-15 of the address. Cartridge code execution is only permited at an address of $JHXX, and RIOT RAM code execution is only permitted at $V8HX. Except as provided below, any JMP, JSR, or RTS will cause a bank switch to the bank specified by the top three bits:
Bank 0 | $1HXX | RAM $1HXX
|
Bank 1 | $3HXX | ROM $0HXX
|
Bank 2 | $5HXX | ROM $1LXX
|
Bank 3 | $7HXX | ROM $1HXX
|
Bank 4 | $9HXX | ROM $2LXX
|
Bank 5 | $BHXX | ROM $2HXX
|
Bank 6 | $DHXX | ROM $3LXX
|
Bank 7 | $FHXX | ROM $3HXX
|
Jumping into RIOT RAM will also trigger a bank switch based upon the upper address bits (e.g. a jump to $08HX will switch to bank 0, $28HX will switch to bank 1, etc.)
Restrictions:
- A JMP or JSR instruction which starts at address N must not jump to an address that is a multiple of 8K bytes from N+2 or N+3, except that a JMP instruction may target the next address in sequence in the same bank.
- Any data fetches from $JHXX must be in the same bank as the code performing them (this restriction applies even when code is running from RIOT RAM; e.g., if RIOT RAM is entered via "JMP $4880", it may may access ROM at $5800-$5FFF, but may not access $1800-$1FFF, $3800-$3FFF, etc.
There are eight switchable data-banking areas, located at $JLXX (i.e. $1000-$17FF, $3000-$37FF, etc.) Each zone is controlled via various hot-spots at $V200-$VF7F. The $1000-$17FF zone is controlled by various hot-spots at $0200-$0F7F, the $3000-$37FF zone is controlled by hot-spots at $2200-$2F7F, etc. Hot-spots are as follows. Note that access to any addresses in the range $V200-$VFFF other than those listed below is forbidden (future functions will likely be added).
Hot-spots (more will follow):
$V000-$V0FF | Zero-page trigger (used to determine bank for (ZP,X) and (ZP),Y address
|
$V100-$V1FF | Stack trigger (used to determine bank for JSR or RTS address)
|
$V280-$V2FF | RIOT registers (no banking action)
|
$V400-$V4FF | Set LSB of banking address (MSB unaffected)
|
$V500-$V5FF | Set MSB of banking address (LSB unaffected)
|
$V600-$V601 | Set bank mode (values below)
|
$V700-$v71F | Map bank to RAM at $0000-$1F00 (LSB cleared)
|
$V880-$V8FF | Executable RIOT RAM
|
$V900-$V97F | Map bank to ROM at $0000-$7F00 (LSB cleared)
|
$VC00-$VCFF | Add $0000-$00FF to bank address
|
$VD00-$VDFF | Subtract $0000-$00FF from bank address
|
$VE00-$VE1F | Add $0000-$1F00 to bank address
|
$VE40-$VE5F | Subtract $0000-$1F00 from bank address
|
Banking modes (more will follow):
0 | Access ROM (read-only; writes forbidden)
|
1 | Access RAM (reads or ST* instructions; read-modify-write forbidden)
|
Implementation Notes
The implementation needs to keep track of the address and mode for each of the eight code banks. It also needs to keep a 16-bit variable called
"LastCodeAddr" and a 32-bit variable called "RecentCode".
Addresses are divided into a few groups. Note that most of the hot-spots are treated similarly; for simplicity, they are simply called "hot-spot" below. All addresses given are physical 13-bit address-bus addresses. An emulator may check upper bits to ensure they are as expected.
- $0000-$00FF
- Wait until address is seen greater than $01FF. Then set RecentCode to ((RecentCode shl 8) or data)
- $0100-$01FF
- If RecentCode[15..8] is $60, set RecentCode to $00004C00 and clear LastCodeAddr. If RecentCode[15..8] is $20, set RecentCode to $0000004C (leave RecentAddr alone). If neither condition applies, clear RecentCode. In any case, wait until address is seen greater than $01FF and handle RecentCode as with $00XX.
- General hot-spot
- Use RecentCode[7..5] to select a one of eight banks, and apply hot-spot behavior to that bank.
- $0880-$08FF
- Banking behavior is like $1800-$1FFF.
- $1000-$17FF
- Use RecentCode[7..5] to select one of eight banks, and treat according to that bank mode. If RAM, if RecentCode[23-16] is $80-$9F, handle as a write; otherwise handle as a read.
- $1800-$1FFF
- If address does matches LastCodeAddr or LastCodeAddr+1, set RecentCode to ((RecentCode shl 8) or data). Otherwise, if RecentCode[23..16] is 0x4C, set code bank based on RecentCode[7..5]; if address hadn't matched, clear RecentCode.
Super-Fetcher mode (preview)
In super-fetcher mode, all addresses $JXXX will be treated identically. The cartridge will maintain its own program counter, and execute two-byte super-instructions. Each super-instruction will generate the data for one or more bus cycles. Some typical super-instructions would be "Load accomulator from fetcher 19", "Store X in COLUP1", and "stuff a 6507 'JMP $1000' instruction. Note that disassembly via normal means will be totally meaningless.