Jump to content
IGNORED

Adding 4A50 Bankswitching to bB


SeaGtGruff

Recommended Posts

Adding 4A50 Bankswitching to bB -- Part 1

-----------------------------------------

 

For a while now, I've been wanting to add support for 4A50 bankswitching to batari Basic. I've decided to work on this project in the Atari 2600 BASIC forum, so anyone who's interested can follow the process to see what's involved, offer comments or suggestions, etc. I apologize in advance for all of the boring parts, because I tend to want to explain things more than I probably should.

 

It must be stressed that this will be an unofficial modification to batari Basic version 1.0, and any specifics about how 4A50 bankswitching works in batari Basic will undoubtedly change quite a bit if or when 4A50 bankswitching support gets officially added to batari Basic.

 

Before beginning, it's a good idea to briefly review 4A50 bankswitching. It's very flexible as far as ways to trigger a bank switch, and I'm focusing exclusively on the traditional "hotspots" method, so by no means is this a comprehensive description. See supercat's blog for additional ways to trigger a bank switch. Note that some of the following information differs from what's in supercat's blog, based on information he sent to me last year. In particular, the changes relate to the amount of ROM, and which portions of ROM are available in which bank regions. I'm hoping supercat will step in and throttle me-- I mean, *correct* me-- if I give out any erroneous information.

 

The 4A50 Cartridge and Bankswitching

------------------------------------

 

The 4A50 cartridge contains 128K of ROM and 32K of RAM. Programs can also be 64K, and possibly 32K as well, but in those cases the ROM would be replicated as needed (i.e., doubled or quadrupled to fill 128K). The following description assumes a ROM size of 128K. The description would be the same for 64K or 32K, except the second 64K segment would be identical to the first 64K segment for a 64K program; and the second, third, and fourth 32K segments would be identical to the first 32K segment for a 32K program.

 

Note that we must use five-digit hex addresses-- or more precisely, 17-bit binary addresses-- to refer to all 128K of ROM: $00000 through $0FFFF for the first 64K, and $10000 through $1FFFF for the second 64K. Of course, this applies *only* to the ORG addresses that tell the assembler where to put the assembled code within the physical ROM image, since the logical addresses within the actual 6502 (or 6507) program code cannot exceed two bytes (lo-byte/hi-byte).

 

The 4A50 bankswitching scheme divides the Atari 2600's 4K cartridge area into four distinct regions:

 

$F000 through $F7FF = Lower bank region (2K).

$F800 through $FDFF = Middle bank region (1.5K).

$FE00 through $FEFF = Upper bank region (256 bytes).

$FF00 through $FFFF = Non-banked or fixed region (256 bytes).

 

(The 6507 CPU has only 13 address pins, so there are several addresses which are equivalent to the ones shown above. Some programmers prefer to use $1000 instead of $F000, but I'm going to use $F000.)

 

There are some restrictions on how this 128K of ROM can be accessed:

 

$00000 through $0FFFF = 1st 64K -- Accessible only through the lower bank region.

$10000 through $17FFF = 1st 32K of 2nd 64K -- Accessible only through the upper bank region.

$18000 through $1FFFF = 2nd 32K of 2nd 64K -- Accessible only through the middle and upper bank regions.

 

For the 2K lower bank region, memory is accessed in 2K segments that begin on 2K boundaries. Thus, the first 64K of ROM is divided up into 32 banks, 2K each, which are accessible only at $F000.

 

The 1.5K middle bank region has a similar rule-- the segments must begin on 2K boundaries-- but only the first 1.5K of each 2K segment is used. The remaining 512 bytes of each 2K segment form two "leftover" pages which are accessible only through the 256-byte upper bank region. Thus, the second 32K of the second 64K of ROM can be divided up into 16 banks, 2K each, of which the first 1.5K is accessible at $F800, and the two leftover pages are accessible only at $FE00.

 

The 256-byte upper bank region may access any 256-byte page within the second 64K of ROM, even including the last 256 bytes which are "frozen" at the fixed region at $FF00. As expected, the 256-byte pages must begin on page boundaries. Thus, the first 32K of the second 64K of ROM is divided up into 32 pages, 256 bytes each, which are accessible only at $FE00; and the second 32K of the second 64K of ROM can also be divided up into 32 pages, accessible at $FE00.

 

On the other hand, the 32K of *RAM* is accessible through any of the three bank regions, subject to the same rules about the 2K boundaries and page boundaries.

 

Given the restrictions described above, each of the three bank regions has a specific amount of ROM or RAM that it can access, as follows:

 

$F000 through $F7FF = Lower bank region -- May access 64K of ROM, or 32K of RAM.

$F800 through $FDFF = Middle bank region -- May access 24K of ROM, or 24K of RAM.

$FE00 through $FEFF = Upper bank region -- May access 64K of ROM, or 32K of RAM.

 

Or, to put it another way:

 

1st 64K of ROM -- All 64K can be accessed only at $F000, 2K at a time.

2nd 64K of ROM -- 1st 32K can be accessed only at $FE00, 1 page at a time.

" " " " -- 24K of 2nd 32K may be accessed either at $F800, 1.5K at a time, or at $FE00, 1 page at a time.

" " " " -- 8K of 2nd 32K can be accessed only at $FE00, 1 page at a time.

 

Accessing the following hotspots will select the indicated ROM segment at the indicated region:

 

+-----------------------------------------+
|   Lower bank region: 2K, $F000-$F7FF	|
+--------------------+--------------------+
| $6E00 = ROM $00000 | $6E10 = ROM $08000 |
| $6E01 = ROM $00800 | $6E11 = ROM $08800 |
| $6E02 = ROM $01000 | $6E12 = ROM $09000 |
| $6E03 = ROM $01800 | $6E13 = ROM $09800 |
| $6E04 = ROM $02000 | $6E14 = ROM $0A000 |
| $6E05 = ROM $02800 | $6E15 = ROM $0A800 |
| $6E06 = ROM $03000 | $6E16 = ROM $0B000 |
| $6E07 = ROM $03800 | $6E17 = ROM $0B800 |
| $6E08 = ROM $04000 | $6E18 = ROM $0C000 |
| $6E09 = ROM $04800 | $6E19 = ROM $0C800 |
| $6E0A = ROM $05000 | $6E1A = ROM $0D000 |
| $6E0B = ROM $05800 | $6E1B = ROM $0D800 |
| $6E0C = ROM $06000 | $6E1C = ROM $0E000 |
| $6E0D = ROM $06800 | $6E1D = ROM $0E800 |
| $6E0E = ROM $07000 | $6E1E = ROM $0F000 |
| $6E0F = ROM $07800 | $6E1F = ROM $0F800 |
+--------------------+--------------------+

+-----------------------------------------+
|  Middle bank region: 1.5K, $F800-$FDFF  |
+--------------------+--------------------+
| $6F10 = ROM $18000 | $6F18 = ROM $1C000 |
| $6F11 = ROM $18800 | $6F19 = ROM $1C800 |
| $6F12 = ROM $19000 | $6F1A = ROM $1D000 |
| $6F13 = ROM $19800 | $6F1B = ROM $1D800 |
| $6F14 = ROM $1A000 | $6F1C = ROM $1E000 |
| $6F15 = ROM $1A800 | $6F1D = ROM $1E800 |
| $6F16 = ROM $1B000 | $6F1E = ROM $1F000 |
| $6F17 = ROM $1B800 | $6F1F = ROM $1F800 |
+--------------------+--------------------+

+-----------------------------------------------------------------------------------+
|					 Upper bank region: 256 bytes, $FE00-$FEFF					 |
+--------------------+--------------------+--------------------+--------------------+
| $6C00 = ROM $10000 | $6C40 = ROM $14000 | $6C80 = ROM $18000 | $6CC0 = ROM $1C000 |
| $6C01 = ROM $10100 | $6C41 = ROM $14100 | $6C81 = ROM $18100 | $6CC1 = ROM $1C100 |
| $6C02 = ROM $10200 | $6C42 = ROM $14200 | $6C82 = ROM $18200 | $6CC2 = ROM $1C200 |
| $6C03 = ROM $10300 | $6C43 = ROM $14300 | $6C83 = ROM $18300 | $6CC3 = ROM $1C300 |
| $6C04 = ROM $10400 | $6C44 = ROM $14400 | $6C84 = ROM $18400 | $6CC4 = ROM $1C400 |
| $6C05 = ROM $10500 | $6C45 = ROM $14500 | $6C85 = ROM $18500 | $6CC5 = ROM $1C500 |
| $6C06 = ROM $10600 | $6C46 = ROM $14600 | $6C86 = ROM $18600 | $6CC6 = ROM $1C600 |
| $6C07 = ROM $10700 | $6C47 = ROM $14700 | $6C87 = ROM $18700 | $6CC7 = ROM $1C700 |
| $6C08 = ROM $10800 | $6C48 = ROM $14800 | $6C88 = ROM $18800 | $6CC8 = ROM $1C800 |
| $6C09 = ROM $10900 | $6C49 = ROM $14900 | $6C89 = ROM $18900 | $6CC9 = ROM $1C900 |
| $6C0A = ROM $10A00 | $6C4A = ROM $14A00 | $6C8A = ROM $18A00 | $6CCA = ROM $1CA00 |
| $6C0B = ROM $10B00 | $6C4B = ROM $14B00 | $6C8B = ROM $18B00 | $6CCB = ROM $1CB00 |
| $6C0C = ROM $10C00 | $6C4C = ROM $14C00 | $6C8C = ROM $18C00 | $6CCC = ROM $1CC00 |
| $6C0D = ROM $10D00 | $6C4D = ROM $14D00 | $6C8D = ROM $18D00 | $6CCD = ROM $1CD00 |
| $6C0E = ROM $10E00 | $6C4E = ROM $14E00 | $6C8E = ROM $18E00 | $6CCE = ROM $1CE00 |
| $6C0F = ROM $10F00 | $6C4F = ROM $14F00 | $6C8F = ROM $18F00 | $6CCF = ROM $1CF00 |
| $6C10 = ROM $11000 | $6C50 = ROM $15000 | $6C90 = ROM $19000 | $6CD0 = ROM $1D000 |
| $6C11 = ROM $11100 | $6C51 = ROM $15100 | $6C91 = ROM $19100 | $6CD1 = ROM $1D100 |
| $6C12 = ROM $11200 | $6C52 = ROM $15200 | $6C92 = ROM $19200 | $6CD2 = ROM $1D200 |
| $6C13 = ROM $11300 | $6C53 = ROM $15300 | $6C93 = ROM $19300 | $6CD3 = ROM $1D300 |
| $6C14 = ROM $11400 | $6C54 = ROM $15400 | $6C94 = ROM $19400 | $6CD4 = ROM $1D400 |
| $6C15 = ROM $11500 | $6C55 = ROM $15500 | $6C95 = ROM $19500 | $6CD5 = ROM $1D500 |
| $6C16 = ROM $11600 | $6C56 = ROM $15600 | $6C96 = ROM $19600 | $6CD6 = ROM $1D600 |
| $6C17 = ROM $11700 | $6C57 = ROM $15700 | $6C97 = ROM $19700 | $6CD7 = ROM $1D700 |
| $6C18 = ROM $11800 | $6C58 = ROM $15800 | $6C98 = ROM $19800 | $6CD8 = ROM $1D800 |
| $6C19 = ROM $11900 | $6C59 = ROM $15900 | $6C99 = ROM $19900 | $6CD9 = ROM $1D900 |
| $6C1A = ROM $11A00 | $6C5A = ROM $15A00 | $6C9A = ROM $19A00 | $6CDA = ROM $1DA00 |
| $6C1B = ROM $11B00 | $6C5B = ROM $15B00 | $6C9B = ROM $19B00 | $6CDB = ROM $1DB00 |
| $6C1C = ROM $11C00 | $6C5C = ROM $15C00 | $6C9C = ROM $19C00 | $6CDC = ROM $1DC00 |
| $6C1D = ROM $11D00 | $6C5D = ROM $15D00 | $6C9D = ROM $19D00 | $6CDD = ROM $1DD00 |
| $6C1E = ROM $11E00 | $6C5E = ROM $15E00 | $6C9E = ROM $19E00 | $6CDE = ROM $1DE00 |
| $6C1F = ROM $11F00 | $6C5F = ROM $15F00 | $6C9F = ROM $19F00 | $6CDF = ROM $1DF00 |
| $6C20 = ROM $12000 | $6C60 = ROM $16000 | $6CA0 = ROM $1A000 | $6CE0 = ROM $1E000 |
| $6C21 = ROM $12100 | $6C61 = ROM $16100 | $6CA1 = ROM $1A100 | $6CE1 = ROM $1E100 |
| $6C22 = ROM $12200 | $6C62 = ROM $16200 | $6CA2 = ROM $1A200 | $6CE2 = ROM $1E200 |
| $6C23 = ROM $12300 | $6C63 = ROM $16300 | $6CA3 = ROM $1A300 | $6CE3 = ROM $1E300 |
| $6C24 = ROM $12400 | $6C64 = ROM $16400 | $6CA4 = ROM $1A400 | $6CE4 = ROM $1E400 |
| $6C25 = ROM $12500 | $6C65 = ROM $16500 | $6CA5 = ROM $1A500 | $6CE5 = ROM $1E500 |
| $6C26 = ROM $12600 | $6C66 = ROM $16600 | $6CA6 = ROM $1A600 | $6CE6 = ROM $1E600 |
| $6C27 = ROM $12700 | $6C67 = ROM $16700 | $6CA7 = ROM $1A700 | $6CE7 = ROM $1E700 |
| $6C28 = ROM $12800 | $6C68 = ROM $16800 | $6CA8 = ROM $1A800 | $6CE8 = ROM $1E800 |
| $6C29 = ROM $12900 | $6C69 = ROM $16900 | $6CA9 = ROM $1A900 | $6CE9 = ROM $1E900 |
| $6C2A = ROM $12A00 | $6C6A = ROM $16A00 | $6CAA = ROM $1AA00 | $6CEA = ROM $1EA00 |
| $6C2B = ROM $12B00 | $6C6B = ROM $16B00 | $6CAB = ROM $1AB00 | $6CEB = ROM $1EB00 |
| $6C2C = ROM $12C00 | $6C6C = ROM $16C00 | $6CAC = ROM $1AC00 | $6CEC = ROM $1EC00 |
| $6C2D = ROM $12D00 | $6C6D = ROM $16D00 | $6CAD = ROM $1AD00 | $6CED = ROM $1ED00 |
| $6C2E = ROM $12E00 | $6C6E = ROM $16E00 | $6CAE = ROM $1AE00 | $6CEE = ROM $1EE00 |
| $6C2F = ROM $12F00 | $6C6F = ROM $16F00 | $6CAF = ROM $1AF00 | $6CEF = ROM $1EF00 |
| $6C30 = ROM $13000 | $6C70 = ROM $17000 | $6CB0 = ROM $1B000 | $6CF0 = ROM $1F000 |
| $6C31 = ROM $13100 | $6C71 = ROM $17100 | $6CB1 = ROM $1B100 | $6CF1 = ROM $1F100 |
| $6C32 = ROM $13200 | $6C72 = ROM $17200 | $6CB2 = ROM $1B200 | $6CF2 = ROM $1F200 |
| $6C33 = ROM $13300 | $6C73 = ROM $17300 | $6CB3 = ROM $1B300 | $6CF3 = ROM $1F300 |
| $6C34 = ROM $13400 | $6C74 = ROM $17400 | $6CB4 = ROM $1B400 | $6CF4 = ROM $1F400 |
| $6C35 = ROM $13500 | $6C75 = ROM $17500 | $6CB5 = ROM $1B500 | $6CF5 = ROM $1F500 |
| $6C36 = ROM $13600 | $6C76 = ROM $17600 | $6CB6 = ROM $1B600 | $6CF6 = ROM $1F600 |
| $6C37 = ROM $13700 | $6C77 = ROM $17700 | $6CB7 = ROM $1B700 | $6CF7 = ROM $1F700 |
| $6C38 = ROM $13800 | $6C78 = ROM $17800 | $6CB8 = ROM $1B800 | $6CF8 = ROM $1F800 |
| $6C39 = ROM $13900 | $6C79 = ROM $17900 | $6CB9 = ROM $1B900 | $6CF9 = ROM $1F900 |
| $6C3A = ROM $13A00 | $6C7A = ROM $17A00 | $6CBA = ROM $1BA00 | $6CFA = ROM $1FA00 |
| $6C3B = ROM $13B00 | $6C7B = ROM $17B00 | $6CBB = ROM $1BB00 | $6CFB = ROM $1FB00 |
| $6C3C = ROM $13C00 | $6C7C = ROM $17C00 | $6CBC = ROM $1BC00 | $6CFC = ROM $1FC00 |
| $6C3D = ROM $13D00 | $6C7D = ROM $17D00 | $6CBD = ROM $1BD00 | $6CFD = ROM $1FD00 |
| $6C3E = ROM $13E00 | $6C7E = ROM $17E00 | $6CBE = ROM $1BE00 | $6CFE = ROM $1FE00 |
| $6C3F = ROM $13F00 | $6C7F = ROM $17F00 | $6CBF = ROM $1BF00 | $6CFF = ROM $1FF00 |
+--------------------+--------------------+--------------------+--------------------+

 

Likewise, accessing the following hotspots will select the indicated RAM at the indicated region:

 

+---------------------------------------+
|  Lower bank region: 2K, $F000-$F7FF   |
+-------------------+-------------------+
| $6E40 = RAM $0000 | $6E48 = RAM $4000 |
| $6E41 = RAM $0800 | $6E49 = RAM $4800 |
| $6E42 = RAM $1000 | $6E4A = RAM $5000 |
| $6E43 = RAM $1800 | $6E4B = RAM $5800 |
| $6E44 = RAM $2000 | $6E4C = RAM $6000 |
| $6E45 = RAM $2800 | $6E4D = RAM $6800 |
| $6E46 = RAM $3000 | $6E4E = RAM $7000 |
| $6E47 = RAM $3800 | $6E4F = RAM $7800 |
+-------------------+-------------------+

+---------------------------------------+
| Middle bank region: 1.5K, $F800-$FDFF |
+-------------------+-------------------+
| $6F40 = RAM $0000 | $6F48 = RAM $4000 |
| $6F41 = RAM $0800 | $6F49 = RAM $4800 |
| $6F42 = RAM $1000 | $6F4A = RAM $5000 |
| $6F43 = RAM $1800 | $6F4B = RAM $5800 |
| $6F44 = RAM $2000 | $6F4C = RAM $6000 |
| $6F45 = RAM $2800 | $6F4D = RAM $6800 |
| $6F46 = RAM $3000 | $6F4E = RAM $7000 |
| $6F47 = RAM $3800 | $6F4F = RAM $7800 |
+-------------------+-------------------+

+-------------------------------------------------------------------------------+
|				   Upper bank region: 256 bytes, $FE00-$FEFF				   |
+-------------------+-------------------+-------------------+-------------------+
| $6D00 = RAM $0000 | $6D20 = RAM $2000 | $6D40 = RAM $4000 | $6D60 = RAM $6000 |
| $6D01 = RAM $0100 | $6D21 = RAM $2100 | $6D41 = RAM $4100 | $6D61 = RAM $6100 |
| $6D02 = RAM $0200 | $6D22 = RAM $2200 | $6D42 = RAM $4200 | $6D62 = RAM $6200 |
| $6D03 = RAM $0300 | $6D23 = RAM $2300 | $6D43 = RAM $4300 | $6D63 = RAM $6300 |
| $6D04 = RAM $0400 | $6D24 = RAM $2400 | $6D44 = RAM $4400 | $6D64 = RAM $6400 |
| $6D05 = RAM $0500 | $6D25 = RAM $2500 | $6D45 = RAM $4500 | $6D65 = RAM $6500 |
| $6D06 = RAM $0600 | $6D26 = RAM $2600 | $6D46 = RAM $4600 | $6D66 = RAM $6600 |
| $6D07 = RAM $0700 | $6D27 = RAM $2700 | $6D47 = RAM $4700 | $6D67 = RAM $6700 |
| $6D08 = RAM $0800 | $6D28 = RAM $2800 | $6D48 = RAM $4800 | $6D68 = RAM $6800 |
| $6D09 = RAM $0900 | $6D29 = RAM $2900 | $6D49 = RAM $4900 | $6D69 = RAM $6900 |
| $6D0A = RAM $0A00 | $6D2A = RAM $2A00 | $6D4A = RAM $4A00 | $6D6A = RAM $6A00 |
| $6D0B = RAM $0B00 | $6D2B = RAM $2B00 | $6D4B = RAM $4B00 | $6D6B = RAM $6B00 |
| $6D0C = RAM $0C00 | $6D2C = RAM $2C00 | $6D4C = RAM $4C00 | $6D6C = RAM $6C00 |
| $6D0D = RAM $0D00 | $6D2D = RAM $2D00 | $6D4D = RAM $4D00 | $6D6D = RAM $6D00 |
| $6D0E = RAM $0E00 | $6D2E = RAM $2E00 | $6D4E = RAM $4E00 | $6D6E = RAM $6E00 |
| $6D0F = RAM $0F00 | $6D2F = RAM $2F00 | $6D4F = RAM $4F00 | $6D6F = RAM $6F00 |
| $6D10 = RAM $1000 | $6D30 = RAM $3000 | $6D50 = RAM $5000 | $6D70 = RAM $7000 |
| $6D11 = RAM $1100 | $6D31 = RAM $3100 | $6D51 = RAM $5100 | $6D71 = RAM $7100 |
| $6D12 = RAM $1200 | $6D32 = RAM $3200 | $6D52 = RAM $5200 | $6D72 = RAM $7200 |
| $6D13 = RAM $1300 | $6D33 = RAM $3300 | $6D53 = RAM $5300 | $6D73 = RAM $7300 |
| $6D14 = RAM $1400 | $6D34 = RAM $3400 | $6D54 = RAM $5400 | $6D74 = RAM $7400 |
| $6D15 = RAM $1500 | $6D35 = RAM $3500 | $6D55 = RAM $5500 | $6D75 = RAM $7500 |
| $6D16 = RAM $1600 | $6D36 = RAM $3600 | $6D56 = RAM $5600 | $6D76 = RAM $7600 |
| $6D17 = RAM $1700 | $6D37 = RAM $3700 | $6D57 = RAM $5700 | $6D77 = RAM $7700 |
| $6D18 = RAM $1800 | $6D38 = RAM $3800 | $6D58 = RAM $5800 | $6D78 = RAM $7800 |
| $6D19 = RAM $1900 | $6D39 = RAM $3900 | $6D59 = RAM $5900 | $6D79 = RAM $7900 |
| $6D1A = RAM $1A00 | $6D3A = RAM $3A00 | $6D5A = RAM $5A00 | $6D7A = RAM $7A00 |
| $6D1B = RAM $1B00 | $6D3B = RAM $3B00 | $6D5B = RAM $5B00 | $6D7B = RAM $7B00 |
| $6D1C = RAM $1C00 | $6D3C = RAM $3C00 | $6D5C = RAM $5C00 | $6D7C = RAM $7C00 |
| $6D1D = RAM $1D00 | $6D3D = RAM $3D00 | $6D5D = RAM $5D00 | $6D7D = RAM $7D00 |
| $6D1E = RAM $1E00 | $6D3E = RAM $3E00 | $6D5E = RAM $5E00 | $6D7E = RAM $7E00 |
| $6D1F = RAM $1F00 | $6D3F = RAM $3F00 | $6D5F = RAM $5F00 | $6D7F = RAM $7F00 |
+-------------------+-------------------+-------------------+-------------------+

 

Of course, we probably wouldn't want to refer to all of those individual addresses to trigger a bank switch. Instead, we'd most likely do something like the following (in assembly):

 

low_ROM_bank = $6E00
;
  LDX desired_bank_number
  LDA low_ROM_bank,X

 

I won't attempt to describe any of the other ways that a bank switch can be triggered in the 4A50 scheme.

 

Problem 1: Organizing the Memory

--------------------------------

 

Before we can add support for 4A50 bankswitching to batari Basic, there are a number of problems, issues, or questions that we must resolve. The first question is, how should we organize the memory in the ROM image?

 

When writing an Atari 2600 program in assembly language, we must tell the assembler where the code is supposed to go in the Atari's memory. Since the Atari 2600 doesn't *have* any built-in memory for storing programs the way a computer does, and instead uses plug-in ROM cartridges that occupy a specific address range, there really isn't much choice of where to put the programs-- the ROM must always start at $F000 (or if it's a 2K ROM, it can start at either $F000 or $F800).

 

With a bankswitched cartridge, things aren't so simple, because the banks must occupy consecutive memory locations within the physical ROM image, but we also need the addresses within each bank to conform to wherever the 6507 CPU will see them when we switch to that bank. Fortunately, the DASM assembler has an ORG statement for defining the physical address within the ROM image, and a RORG statement for defining the logical address of the code. With a 2K or 4K ROM image, the ORG and RORG addresses can be the same, in which case we could just omit any RORG statements. But with bankswitching, we need to use both types of statements.

 

For example, let's consider a 16K cartridge that uses F6 bankswitching. If we want the 16K ROM image to end at address $FFFF, then we start at address $C000. But in F6 bankswitching, each bank is a 4K segment that gets switched in at address $F000. Thus, we could use the following ORG and RORG statements:

 

   ORG  $C000; Start of 1st 4K bank
  RORG $F000; Each 4K bank will be accessed at $F000
;
  ORG  $D000; Start of 2nd 4K bank
  RORG $F000
;
  ORG  $E000; Start of 3rd 4K bank
  RORG $F000
;
  ORG  $F000; Start of 4th 4K bank
  RORG $F000

 

We could actually begin with an ORG statement of $0000, or $1000, or anything else, as long as we use RORG statements that begin each 4K bank at $F000, or some address equivalent to $F000 in the 6507's 13-bit address space. For example, some programmers like to use $1000 for the first RORG statement, $3000 for the second RORG statement, $5000 for the third RORG statement, etc., so each bank contains unique logical addresses. However, 4A50 bankswitching has too many banks for us to assign unique logical addresses to each one, so we'll just reuse the same logical addresses as appropriate.

 

In some bankswitching schemes, a bank can be switched into only one possible locations, so there's no choice of what RORG addresses to use. But in other bankswitching schemes, a bank can be switched into two or more possible locations, so there are two or more possible RORG addresses for that bank. In the 4A50 bankswitching scheme, most of the banks or pages must be switched into specific locations (as described previously), but some of the banks or pages could be switched into either of two different locations. We need to know which of these locations will be used *before* we compile the program, so the DASM assembler will use the correct logical addresses.

 

To keep things as simple as possible, I'm assuming that the 24K of ROM which *can* be accessed in the middle bank region, *will* be accessed there. That way, if someone decides to put program code in those segments, it will execute correctly when switched into the middle bank region. This won't prevent anyone from putting *data* in those segments and accessing them one page at a time in the upper bank region, as long as they refer to the data using addresses that fall within the upper bank region. Furthermore, if we're careful about how we let the programmer declare the start of a new bank, then the programmer can choose whether certain portions of code or data will be accessed in the middle bank region or upper bank region. In any case, our *default* memory configuration will be as follows:

 

   ORG $00000; Start of 1st 64K (32 banks of 2K)
  RORG $F000; Each 2K bank will be accessed at $F000
;
  ORG $00800; 2nd bank of 1st 64K
  RORG $F000
;
; etc.
;
  ORG $0F800; Last bank of 1st 64K
  RORG $F000
;
;----------------------------------------------------------------
;
  ORG $10000; Start of 1st half of 2nd 64K (128 pages)
  RORG $FE00; Each page will be accessed at $FE00
;
  ORG $10100; 2nd page of 2nd 64K
  RORG $FE00
;
; etc.
;
  ORG $17F00; Last page of 1st half of 2nd 64K
  RORG $FE00
;
;----------------------------------------------------------------
;
  ORG $18000; Start of 2nd half of 2nd 64K (16 banks of 2K)
  RORG $F800; 1st 1.5K of each bank will be accessed at $F800
;
  ORG $18600; 1st page of leftover ROM
  RORG $FE00; Each leftover page will be accessed at $FE00
;
  ORG $18700; 2nd page of leftover ROM
  RORG $FE00
;
  ORG $18800; 2nd bank of 2nd half of 2nd 64K
  RORG $F800
;
  ORG $18E00; 1st leftover page of 2nd bank
  RORG $FE00
;
  ORG $18F00; 2nd leftover page of 2nd bank
  RORG $FE00
;
; etc.
;
  ORG $1F800; Last bank of 2nd 64K
  RORG $F800
;
  ORG $1FE00; 1st leftover page of last bank
  RORG $FE00
;
;----------------------------------------------------------------
;
  ORG $1FF00; Last page of ROM
  RORG $FF00; Fixed at $FF00
;
  ORG $1FFFA; Beginning of 6502 vectors
  RORG $FFFA
;
NMI_Vector
  WORD $4A50; Identifies this as a 4A50 cartridge
;
RES_Vector
  WORD Boot_Up; Points to the program's "Boot_Up" routine
;
IRQ_Vector
  WORD BRK_Handler; Points to an optional "BRK_Handler" routine
;
END

 

The specs say $1FFF8-$1FFF9 should contain the 4A50 version number, stored lo-byte/hi-byte. The current version number is $0001, but supercat has stated that it's being ignored right now, so I've simply omitted it above. Furthermore, supercat has stated that the version number, and possibly also the $4A50 identifier, might get moved somewhere below $1FF00, to avoid taking up any more of the last 256 bytes than absolutely necessary, since those are the only bytes of ROM which are always accessible to all banks and pages within the cartridge. The names of the "Boot_Up" and "BRK_Handler" routines are given for descriptive purposes only, and the "BRK_Handler" routine is optional anyway, since most programs just put the same address in both the RES and IRQ vectors (and sometimes in the NMI vector as well).

 

Obviously, we'll want the actual organization of memory to be as transparent to batari Basic programmers as possible, so they can write programs without having to worry about these sorts of details-- after all, that's probably one reason why they're programming in batari Basic instead of in assembly language!

 

On the other hand, anyone who's writing a bankswitched program-- whether in batari Basic or assembly-- should be at least *somewhat* aware of how the banks are organized in whichever bankswitching scheme they're using, to help them code their program as efficiently as possible. With 4A50 bankswitching, this will be especially important.

 

Up next: The Includes File and the Include Files!

 

Michael

Edited by SeaGtGruff
Link to comment
Share on other sites

The $1E00-$1EFF region can access the entire upper 64K of ROM. Your hotspot table reflects this, but your description does not.

 

It's not 'normal' to access ROM code within the $1E00-$1EFF area, though it can be useful to access ROM data there. There are some advanced tricks that involve running code at $1E00-$1EFF and using the $7F00-$7FFF hotspots, but those are a subject for another day.

 

Even if you don't plan to use the $F4-$FF or $7F00-$7FFF hotspots, you need to be careful of them. Stack accesses $01F4-$01FF are not hotspots; running code from $1F00-$1FFF will effectively trigger random bankswitches of the $1E00 area.

 

I may be looking to add some improvements to 4A50. The current version would be available to anyone who wants, but some other goodies would be available as well.

Link to comment
Share on other sites

The $1E00-$1EFF region can access the entire upper 64K of ROM. Your hotspot table reflects this, but your description does not.

Actually, the description does reflect that, but the wording is too confusing. :)

 

It's not 'normal' to access ROM code within the $1E00-$1EFF area, though it can be useful to access ROM data there. There are some advanced tricks that involve running code at $1E00-$1EFF and using the $7F00-$7FFF hotspots, but those are a subject for another day.

I don't think I could stop anyone from putting code there if they really want to. :) In fact, if you set the middle and upper bank regions appropriately, you could access 1.75K of consecutive ROM code at $F800-$FEFF.

 

Even if you don't plan to use the $F4-$FF or $7F00-$7FFF hotspots, you need to be careful of them. Stack accesses $01F4-$01FF are not hotspots; running code from $1F00-$1FFF will effectively trigger random bankswitches of the $1E00 area.

Oh crap. Running code at $1F00-$1FFF will trigger random bankswitches? I presume that also means $FF00-$FFFF? It was my intention to put a minimal "Boot_Up" routine there, preferably as close to the 6502 vectors as possible, to switch to the first bank and then immediately jump to a longer "Start_Up" routine in the first bank, as follows:

 

Boot_Up
  LDA $6E00
  JMP $F000

 

Oh wait, you said random bank switches of the upper bank region. So that's okay, as long as "Boot_Up" doesn't try to set the upper bank region.

 

...Except, I was thinking of putting a generic bankswitching subroutine in that last page, so code in any bank (or page) can call a subroutine that's in any other bank (or page)-- maybe something like what bB does with F8/F6/F4 bankswitching. I guess I'll have to be careful there. Or maybe I could put the generic bankswitching subroutine down in zero-page RAM? I expect to move some of batari Basic's "system variables" up into the extra 32K of RAM, as long as it doesn't screw up any load/store timings-- although I do expect to write a new canned kernel to take advantage of the extra RAM, which means I'll be able to fiddle with the kernel's scanline timings all I want. :ponder:

 

In any case, I definitely need to read up on those other ways of triggering bank switches.

 

I may be looking to add some improvements to 4A50. The current version would be available to anyone who wants, but some other goodies would be available as well.

Improvements would be great! And I think you should do it sooner rather than later, before any 4A50 support is officially added to bB, or for that matter to Stella, etc.

Link to comment
Share on other sites

Adding 4A50 Bankswitching to bB -- Part 2

-----------------------------------------

 

Now that we've briefly described 4A50 bankswitching, and thought about how we should organize the memory, we're ready to start tackling the first *real* problem.

 

Problem 2: The Includes File and the Include Files

--------------------------------------------------

 

The batari Basic language uses a number of "include" files. They assign symbolic names to various memory addresses, define useful macros, contain important routines, define the shapes of the digits used in the score display, set the starting and ending addresses of the program code, etc. When you compile a batari Basic program, some of those files are automatically included with the assembly code that's generated from your batari Basic code-- hence the term "include files."

 

To a great extent, these include files are the real heart and soul of batari Basic. By modifying the include files, or replacing them with completely different ones, you can customize batari Basic in all sorts of ways. Furthermore, even if you use only the provided include files, the act of setting certain options in a batari Basic program-- specifically, the ROM size, whether or not the Superchip will be used, and the type of kernel-- causes the batari Basic compiler to include some of the include files and exclude others. Additional include files can be optionally included as needed by specifying them within the batari Basic program itself.

 

There is a *list* of which include files are to to be included, and that list is known as the "includes file." The batari Basic version 1.0 installation package came with five such includes files:

 

default.inc

bankswitch.inc

superchip.inc

multisprite.inc

multisprite_bankswitch.inc

 

Unfortunately, there happens to be a bug in the version 1.0 compiler that causes the wrong includes file to be chosen in some situations (as we'll see in a moment). Also, if you observe that there's a pattern to the names of these includes files, then you can see that there should have been a *sixth* includes file:

 

multisprite_superchip.inc

 

However, that includes file apparently hadn't been created and/or tested at the time version 1.0 was being released, so it was left out.

 

Anyway, the following "set" commands will cause the compiler to automatically select the indicated includes files:

 

   set romsize 2k : rem * or 4k
  rem * default.inc will be selected

 

   set romsize 8k : rem * or 16k or 32k
  rem * bankswitch.inc will be selected

 

   set romsize 8kSC : rem * or 16kSC or 32kSC
  rem * superchip.inc will be selected

 

   set romsize 2k : rem * or 4k
  set kernel multisprite
  rem * multisprite.inc will be selected

 

   set romsize 8k : rem * or 16k or 32k
  set kernel multisprite
  rem * bankswitch.inc will be selected : rem * ERROR!

 

   set kernel multisprite
  set romsize 8k : rem * or 16k or 32k
  rem * multisprite.inc will be selected : rem * ERROR!

 

   set romsize 8kSC : rem * or 16kSC or 32kSC
  set kernel multisprite
  rem * superchip.inc will be selected : rem * ERROR!

 

   set kernel multisprite
  set romsize 8kSC : rem * or 16kSC or 32kSC
  rem * multisprite.inc will be selected : rem * ERROR!

 

Fortunately, version 1.0 contains an extremely helpful command that lets us overcome this compiler bug by telling batari Basic which includes file we want it to use. It's also easy to create the missing multisprite_superchip.inc file, and then tell batari Basic to use it where appropriate:

 

   includesfile multisprite_bankswitch.inc
  set romsize 8k : rem * or 16k or 32k
  set kernel multisprite
  rem * multisprite_bankswitch.inc will be used

 

   includesfile multisprite_superchip.inc
  set romsize 8kSC : rem * or 16kSC or 32kSC
  set kernel multisprite
  rem * multisprite_superchip.inc will be used

 

This "includesfile" command is the key to (unofficially) adding 4A50 bankswitching to batari Basic, because it lets us override the compiler's automatic choice of which includes file should be used. Thus, if we create one or two new includes files for 4A50 bankswitching, we can just tell batari Basic which one to use:

 

   includesfile default_4a50.inc

 

   includesfile multisprite_4a50.inc

 

For the time being, let's focus on default_4a50.inc, which will use the standard kernel with 4A50 bankswitching. After we get the standard kernel working, we can tackle multisprite_4a50.inc. However, in the long run we'll really want to create a brand new kernel that takes better advantage of 4A50 bankswitching. Thus, default_4a50.inc and multisprite_4a50.inc will simply be temporary interim solutions as we try to make the transition to a full-blown 4A50-compatible batari Basic.

 

Over the past week, I've tinkered around with the includes file and the include files. I'll just give a brief synopsis, rather than trying to describe everything in detail.

 

My first attempt was to put batari Basic's "system routines" (i.e., the "canned" kernel and the other included routines) at ORG $00000, RORG $F000, and then put the score graphics at ORG $1FEB0, RORG $FEB0. That way, everything in between could be the user's program. I got that working with a very simple test program, but then I had second thoughts.

 

My original plan had actually been to put the system routines at RORG $F800-- i.e., in the middle bank region-- because the system routines generally take up less than 1.5K. By putting them in the 1.5K middle bank region, we can try to leave them alone, and focus on using the 2K lower bank region for the user's program. That way, the kernel can stay in memory as much as possible, and we won't need to worry about switching to the kernel's bank whenever we need to call drawscreen or any of the other included routines.

 

Consequently, my second attempt uses the following plan:

 

$00000 through $0FFFF = 64K, dedicated to the user's program code -- 32 banks of 2K.

$10000 through $1F7FF = 62K, dedicated to graphics and other data -- 62 pages of 256 bytes.

$1F800 through $1FDFF = 1.5K, dedicated to the batari Basic's system routines.

$1FE00 through $1FEFF = 256 bytes, dedicated to the score graphics.

$1FF00 through $1FFFF = 256 bytes, dedicated to the last page of ROM.

 

At the moment, things aren't working according to the final plan, because the graphics data is actually being put in the lower bank region with the program code, and I haven't yet tried to resolve the issue of getting batari Basic to compile the graphics data into the 62K that will be accessed through the 256-byte upper bank region.

 

Anyway, one of the issues I ran into is that the 4A50 bankswitching scheme uses some zero-page hotspots to trigger bank switches-- namely, $71 through $7F, and $F4 through $FF. It turns out that the $71 through $7F locations aren't a problem, because they don't correspond to any of the zero-page RAM addresses, and are simply mirrors of TIA read/write registers. Specifically, $71 through $7D are mirrors of some TIA read registers, but we don't need to worry about them, because if we *write* to $71 through $7D, the Atari will use the TIA *write* registers rather than the read registers-- and $71 through $7F don't mirror any of the actual TIA write registers.

 

Unfortunately, we *do* need to worry about $F4 through $FF, because they're part of the zero-page RAM, and batari Basic uses them for some of its variables. I modified the header file to redefine those addresses as $01F4 through $01FF, since the zero-page RAM is mirrored at $0180 through $01FF, and those addresses will *not* trigger bank switches. However, it turns out that the standard kernel uses two of those addresses-- $F4 (stack1) and $F5 (stack2)-- and redefining those two variables as $01F4 and $01F5 causes timing errors in the kernel, resulting in a garbled playfield display. Therefore, I ended up changing the kernel to use $F0 (aux1) and $F1 (aux2) instead of $F4/$01F4 (stack1) and $F5/$01F5 (stack2).

 

Note that this issue will eventually go away once we create a 4A50-specific kernel, because we'll be able to move the playfield into the 4A50's expansion RAM, which will free up 48 bytes of zero-page RAM, allowing us to move any variables out of the $F4 through $FF "danger zone," while simultaneously giving us *more* zero-page RAM for user variables, as well as more zero-page RAM for the stack.

 

For the time being, I'm posting the seven files that I modified (or rather, the seven custom files, since they don't have the same names as the original files), along with my little test program. A few points need to be stressed:

 

(1) These are just temporary versions of the files; any final versions are likely to be different-- and any *official* support of 4A50 bankswitching in batari Basic may end up being very different than what I'm doing unofficially.

 

(2) These modifications aren't actually usable yet, because the graphics won't compile into the desired location, and there is no suitable method yet for switching banks; so in reality, these modifications will work only as long as the user's program code and graphics data will fit within the first 2K of ROM.

 

(3) You can compile my little test program by putting the seven custom files in your batari Basic includes directory, but the only way you'll be able to *run* it is by using the modified version of z26 that supercat posted in his blog.

 

Up next: Still more work on the include files.

 

Michael

 

post-7456-1203326134_thumb.png

test_includes_file.bas

test_includes_file.bas.bin

4a50_includes.zip

Link to comment
Share on other sites

I may be looking to add some improvements to 4A50. The current version would be available to anyone who wants, but some other goodies would be available as well.

Improvements would be great! And I think you should do it sooner rather than later, before any 4A50 support is officially added to bB, or for that matter to Stella, etc.

Too late, Stella now has 4A50 support (and it works with the ROM you posted too). It even autodetects this ROM as 4A50 :)

Link to comment
Share on other sites

Stella now has 4A50 support (and it works with the ROM you posted too). It even autodetects this ROM as 4A50 :)

Cool! Can I get it from the daily builds at the Stella site? I'm going now to look...

 

Michael

Unfortunately, the daily snapshot site hasn't been working for months. I plan to fix it, and move it to one of my own servers, but since I didn't get any feedback on it not working, I didn't think there was much interest.

Link to comment
Share on other sites

Unfortunately, the daily snapshot site hasn't been working for months. I plan to fix it, and move it to one of my own servers, but since I didn't get any feedback on it not working, I didn't think there was much interest.

Yeah, I saw yesterday that it hadn't been updated in a long time, and the link in the readme didn't work. I think the nightly builds are mostly of interest to the Stella development team (which unfortunately seems to be just you these days), as the typical end user should probably be sticking with the latest version release. But from your announcement on the Stella list, it sounds like you're close to releasing the next version very soon now, despite the fact you haven't been able to add in all of the bug fixes or enhancements yet that you've been hoping to squeeze in there. So I'll expect it when I see it! :) And in the meantime, anyone interested in 4A50 still has the modified z26 that John compiled.

 

And I want to give you a *BIG* "Thank you!" for all the work you've been doing on Stella! :thumbsup:

 

Michael

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...