Joined: Thu May 28, 2009 9:46 pm Posts: 8504 Location: Midwestern USA
|
hth313 wrote: Here is a bug fix release version 3.3.2 of my C compiler for the 65816 and the 6502. Links for 65816User guide https://tinyurl.com/4ws689bhInstaller Arch Linux https://tinyurl.com/4krtbxxyInstaller Debian https://tinyurl.com/yysf79c5Installer macOs https://tinyurl.com/88hu5r3cLinks for 6502User guide https://tinyurl.com/f3vcwdy2Installer Arch Linux https://tinyurl.com/yedpfp54Installer Debian https://tinyurl.com/caw68y6tInstaller macOs https://tinyurl.com/469mjeaa Although I've done virtually nothing with C in a 6502-family application, I've been looking at this compiler with some interest, as there may well come a time where I'd do some C development for the 816. The compiler is clearly real labor of love and appears to have been logically designed. I also like that it appears to be usable on Linux without regards to the particular distro being used (I'm a SuSE advocate, FWIW). And unusual for more than a little software of this type, the user guide is well-organized.
Speaking of the user guide, I did see some information that was presented about the 65C816's memory model that I'd like to discuss in the interest of clarity. Also, I have some curiosity about how the compiler resolves some things that are more-or-less 65C816-specific. Please note that the following is not criticism, just an attempt to reconcile what the guide says with what I know about the 816's behavior.
On page 13 of the guide, there is:Even though the address space 16MB, it can be seen as a sequence of 64K address ranges. That statement may be confusing to someone who is not well-acquainted with the 65C816 and its addressing model.
The sequence of 64KB address ranges is of concern with program execution, direct page and stack access, MPU vector access, and non-indexed indirect vector accesses. The 64KB boundary regarding program execution is due to PB (program bank) not incrementing when PC (program counter) wraps from $FFFF to $0000. That and the other items are artifacts of the 65C816 being designed to emulate a 65C02 at reset—the C02, of course, has no concept of extended addressing, as well as being designed to run 65C02 code while operating in native mode.
At the programmer's discretion, data access may be limited to a 16-bit range—in which case, the value in DB (data bank register) applies and conventional 6502 addressing modes are used, or the entire 24-bit address space may be treated as linear with the use of "bank-agnostic" code.While address calculations can be made to cross 64K page boundaries, it is not an efficient way to utilize the 65816. The use of the word "page" in the above is incorrect and this incorrect usage is prevalent throughout the guide.
In 6502-family architecture, a "page" is defined as 256 contiguous bytes, not 64KB. In 65C816 architecture, a "bank" is defined as 256 contiguous pages, with any given bank starting at $xx0000, where $xx is the bank number, $00-$FF.¹
As for the statement about efficiency, I question that. The 65C816 assembly language makes it possible to treat the full address space as linear space for data purposes, using succinct code. This possibility is fully exploited if "long" pointers are handled as 32-bit entities instead of 24-bit so frequent use of REP and SEP in pointer arithmetic sequences can be avoided.²A single function needs to be shorted(sic) than 64K, data objects should also be kept smaller than 64K whenever possible. I'm a little confused by what is being said here.
If "function" refers to program code, yes, it has to be limited in size to fit into one bank. In practice, that is almost never a limitation, as few 6502-family programs ever approach the 64KB size limit. Should an 816 program be so large that it exceeds 64KB of code, part of it will have to be run in a different bank. In such a case, I could see where the main-line routines would be in bank B and the associated subroutines in bank B+1 for convenience in loading from mass storage, with JSL instructions being used to call the subroutines in B+1. There is a slight performance penalty associated with this programming model, but it does allow programs to span hundreds of kilobytes if so desired.
The only data objects that would be tied to a bank would be those that are to be accessed using 16-bit addressing. My approach is to place static data, e.g., lookup tables, and dynamic data structures, e.g., buffers, specific to the program being run in the same bank as the code and at the beginning of the program, execute PHK - PLB to set the data bank to the program bank. Hence a 16-bit access is implicitly in the code's bank, giving fastest access to an object. That model is also usable in "far" subroutines by preserving PB prior to the PHK - PLB sequence.There are 256 such 64K pages on the 65816 numbered 00 to FF (hexadecimal). The first page (00) is special in that the system stack and the direct page must be located in it. Certain vectors, such as the reset and interrupt vectors also reside in page 00. Again, "page" is being incorrectly used in place of "bank." What is being referred to as "page (00)" should be "bank (00)", although that is not the correct way to refer to any bank (no indirection is involved). Page $00 always refers to the lowest page in physical RAM bank $00. That is a very important distinction.The B or data bank register is an 8 bits register that points to the active near page. The 65C816's has a B-accumulator (usually referred to as .B or BR in machine language monitors) when the accumulator/memory m flag bit in the status register (SR) is a 1, setting the accumulator size to eight bits. The data bank register is referred to as DB or DBR (I prefer the former) to avoid confusion with the name of the B-accumulator. Also, DB points to a "bank," not a "page." DP (direct page register) is the only 65C816 register that points to a "page."
On page 14, there is this:In the small data model the near area must be the same page(sic) as the stack (00). This is because the default pointer size is 16 bits wide and can only point to a single 64K page. As we may want to point to object in the stack, they must be in the same page. Is the "stack" being referred to in the above the MPU's hardware stack or a LIFO in bank $00 being treated as a stack, e.g., like the data stack commonly used in Forth? If the latter, perhaps that should be clarified for the benefit of the reader.
If the former, I'm a little confused by the As we may want to point to object in the stack, they must be in the same page. statement. Wouldn't such an object be accessible with <offset>,S (stack-relative) addressing, which implicitly refers to bank $00 and does not involve DB in any way?
On page 15:A dynamically allocated variable is allocated from a heap using the malloc function...Note: Dynamically allocated variables are a potential problem in memory constrained systems if the program is left running for a long time due to heap fragmentation. What defines a "memory-constrained" system? In other words, is there a total RAM threshold below which a system becomes memory-constrained from the compiler's perspective? Aside from the possibility of the heap becoming fragmented from numerous malloc() and free() calls (which is really a separate issue, I think), what other (possibly bad) things could happen in a memory-constrained environment? Also, how and when is the size of the heap determined? Is the heap limited to a single bank or can it be defined to span multiple banks to accommodate malloc() calls that request more than 64KB?
Also on page 15:The tiny address space is 256 bytes of memory located somewhere in the first 64K of memory. It has an address range 0x00-0xff...Being only 256 bytes and also shared with pseudo registers, you are somewhat limited on how much you can store in the tiny area. I interpret the above to mean a data structure could be placed on direct page, which is unusual in most code—direct page is generally considered to be too valuable to be used in that fashion, except in narrowly-defined cases. Please clarify. Also, if I write REGISTER INT X=0 in my C program, where is X being stored?
On page 16:The huge address space covers the entire 16MB address range. The maximum size of an object is 16MB (minus one byte). I've not seen any 65C816 system with more than 4MB of RAM. How does the compiler reconcile the theoretical 16MB address space to the actual address space? For example, if I were to compile a program using "huge" addressing to run on my POC V1.3 unit (128KB, of which 64KB are in bank $01), how would the program "know" that the highest-accessible physical address is $01FFFF? Also, does the compiler have a way to handle memory "holes" caused by idiosyncratic address decoding?
On page 74:Assembly language files usually have the file extension .s or .asm, but it varies wildly as assembly language itself is not standardized in any way. Although source code file naming conventions do vary, the 6502 assembly language itself is standardized—MOS Technology wrote the original standard, which compliant assemblers will implement. The 65C816 assembly language is also standardized—WDC promulgates that standard in the data sheet. The language standards intentionally ignore assembler pseudo-ops, which is nothing unique to the 6502 universe.
Regrettably, there are all sorts of 6502 assembly language bastardizations in hobbyist-developed assemblers, some borne of ignorance of the MOS Technology/WDC standards that have existed some 46 years, and others the result of the "I don't like it, so I'm going to come up with my own 'standard'." line of thinking. So it probably should be clarified that there is a standard, even though it may not seem so to the casual observer.
On page 80:rodata - An initialized data section in read only memory (ROM). Can or does the compiler treat the run-time data that was generated during compilation, e.g., a lookup table associated with, say, a switch() statement, as read-only data, as in:
Code: switch (i) { case 1: printf("i = 1.\n"); break; case 2: printf("i = 2.\n"); break; ...etc... default: printf("i is not 1 or 2.\n"); } At least in assembly language, there would have to be some sort of address lookup table to vector the MPU according to the result of the switch() statement. Would your compiler place that lookup table in the rodata segment, even though no ROM may be involved?
—————————— ¹Technically speaking, a 65C816 "bank" is really a "segment," as it isn't accessed through an address space "window," as banking would be done in an eight-bit system to give access to more than 64KB.
²I recommend to anyone writing firmware and/or a operating system kernel for a 65C816 adopt the use of 32-bit pointers for parameter-passing. The underlying code will be smaller and faster, at the expense of slightly more direct page usage.
_________________ x86? We ain't got no x86. We don't NEED no stinking x86!
Last edited by BigDumbDinosaur on Tue Aug 31, 2021 7:40 pm, edited 1 time in total.
|
|