Keil Logo

C51: Creating Code Banking Programs


How do I create a code banking application?


To create a code banking application you must have 2 things. A Common Code area and the Code Bank area. The Common code area is always "mapped in". The Code Bank area is the area that is swapped when you switch to a different bank.


The Common Code area MUST INCLUDE the reset vector, the interrupt vectors, and the interrupt routines. This is because the 8051 does not inherently know how to deal with bank switching. Additionally, code that you call from all over your program should be included in the common area to make it run as fast as possible. Additionally, the linker locates the bank switching logic and jump table in the common area.


The Code Bank area contains the code banks starting with code bank 0, 1, 2, and so on. When you have a function in bank 0 that calls a function in bank 0, your code does not switch banks. It jumps directly to that function. In other words, intra-bank calls are generated just like non-code-banking function calls.

If a function in bank 0 calls a function in bank 1, a little more complexity is involved. The call in your bank 0 function jumps to a jump table in the common area. This jump table contains the bank switch and jump logic to transfer control for all inter-bank function calls. So, if you have 32 code banks and you have exactly 1 function in each bank, there will be 32 entries in the jump table.

The jump table solution takes a few extra cycles to switch the bank, save the current bank (so we can return to the calling function), and jump to the target routine.


When organizing a program for bank switching, you should combine co-dependant routines into each bank. For example, if your application has a run mode and an edit mode for setting the configuration, you should try to put all of the run-mode routines in a single bank and all of the edit mode routines in another bank. When the run mode routines call each other, there is no bank switching going on and the application runs fast. The same applies to the edit mode routines.

If you have general-purpose routines that are called from everywhere, locate them in the common area for fastest access. When a function in a code bank calls a function in the common area, there is no bank switching (since the common area is ALWAYS mapped in).

Remember the following rules.

  • Inter-bank calls are slow.
  • Intra-bank calls are fast.
  • Calls to functions in the Common area are fast.


The next trick is how to assign memory areas for the common area and the bank area.

In the past, when EPROMs were expensive, people used 27256's (32K EPROM) for their program memory. A single 27256 was used for the common area and separate 27256's were used (1 for each 32K code bank). So a target board with 4 code banks used 5 EPROMs, 1 for the common area and 4 for the code banks (0, 1, 2, and 3). In this case, the hardware dictated the size of the common area and the code banks (sort of).

Today, most people use a large FLASH device or a large 2meg EPROM. With these devices, it is much easier to mentally configure and re-configure the size of the common area and the size of the code banks. Since there is only one device, you can literally just connect the port pins to the address lines of the part. The biggest problem is that you have to program 256K Bytes of data into the part.

You must tell the linker where the code banking area is. This implies that the common area is the area of memory from 0x0000 to the beginning of the code banking area. For example, if you specify that the bank area is from 0x4000 to 0xFFFF, that gives 48K for the code banks and only 16K for the common area (0x0000 to 0x3FFF).


When you compile and link your code banking program, the linker creates a special OMF file. This file contains code for each code bank and must be split up into OMF51 object modules for each code bank using OC51. Then, you must make Intel HEX files from each of those OMF-51 files using OH51. So, for a program with 4 code banks, this process emits 4 HEX files: PROGRAM.H00, PROGRAM.H01, PROGRAM.H02, and PROGRAM.H03.

Assuming that the bank area is from 0x8000 to 0xFFFF (which implies that the common area is from 0x0000 to 0x7FFF), each HEX file will have the common area from 0x0000 to 0x7FFF. Yes, the common area is stored in EACH HEX FILE. Each HEX file will also have the code bank code from 0x8000 to 0xFFFF. The file extension tells the number of the code bank. For example, H00 is bank 0, H01 is bank 1, and so on.


Once you have the HEX files, the next step is programming EPROMs.

If you use separate EPROMs for the common and bank areas, first pick one of the HEX files (it doesn't matter which one) and program the COMMON EPROM using the data from 0x0000 to the end of the common area. Then, for each code bank, load the HEX file into your device programmer and program each code bank EPROM. Remember that the code for each code bank is at offset 0x8000 in the HEX file. This needs to be located at offset 0x0000 in your code bank EPROMs.

If you use a single EPROM for the common and bank areas, load the H00 file and program it into the EPROM at offset 0x000000. Load the H01 file and program it into the EPROM at offset 0x010000. Load H02 and program it at offset 0x020000. Repeat this for each bank. When you are through (assuming the bank area is from 0x8000 to 0xFFFF), the EPROM contains the following:

0x000000 - 0x007FFF Common Area
0x008000 - 0x00FFFF Code Bank 0
0x010000 - 0x017FFF Common Area
0x018000 - 0x01FFFF Code Bank 1
0x020000 - 0x027FFF Common Area
0x028000 - 0x02FFFF Code Bank 2
0x030000 - 0x037FFF Common Area
0x038000 - 0x03FFFF Code Bank 3

When your program selects BANK 0, the 64K of memory from 0x000000 to 0x00FFFF is visible. The common area goes from 0x0000 to 0x7FFF and bank 0 goes from 0x8000 to 0xFFFF. When you select bank 1, the EPROM memory from 0x010000 to 0x01FFFF is visible with the common area from 0x0000 to 0x7FFF and bank 1 from 0x8000 to 0xFFFF. Obviously, the larger the common area is, the more of the EPROM is wasted. There are tricks you can use to decode the EPROM so that the memory map is a little different, but that is beyond the scope of this description.


The best first step should be a small one. Create a program with a main function, function a (in bank 0), and function b (in bank 1), and get that working. Once you do that, everything else will fall into place.



Last Reviewed: Thursday, February 25, 2021

Did this article provide the answer you needed?
Not Sure
  Arm logo
Important information

This site uses cookies to store information on your computer. By continuing to use our site, you consent to our cookies.

Change Settings

Privacy Policy Update

Arm’s Privacy Policy has been updated. By continuing to use our site, you consent to Arm’s Privacy Policy. Please review our Privacy Policy to learn more about our collection, use and transfers
of your data.