|
From: John Brandwood <john@te...> - 2016-06-22 22:43:25
|
Hi guys, I've been using CA65 for a while now, and it's finally time to bring up the problem of zero-page addressing. On the HuC6280, the regular zero-page addressing modes actually refer to locations $2000-$20FF. As such, it's more like the 6809's direct-page addressing ... but limited to a single page. CA65 is assuming that all one-byte addressing modes refer to a location in the range $0000-$00FF, i.e. the high byte is zero. I've been trying to fix this so that CA65 will correctly use the direct-page/zero-page addressing for constant symbols in the range $2000-$20FF, and could use some advice. It only looks like I have to modify CA65 in 3 locations to get this to work. 1) in studyexpr.c at approx line 449 when CA65 is deciding whether a constant symbol should use 1 or 2 byte addressing. This looks like a safe change to make. 2) in segment.c at approx line 394 when CA65 is checking for range errors. Disabling this check for the HuC6280 seem like the only option. It doesn't look like this would to cause any problem, because the range checking is done again anyway during code output in objcode.c 3) in objcode.c at approx line 71 when the assembler is emitting an instruction with a 1 byte argument. This is where I have a question. I can disable the range check for the HuC6280, but then we'd lose the safety of range checking on immediate instructions. There's no addressing-mode information being passed into Emit1(), so the only thing that I've got to differentiate between a direct-page reference and an immediate byte is the opcode. Does it sound OK to folks for me to put a 256-byte opcode-to-addressing-mode table into objcode.c to see if the check should be for $2000-$20FF (direct-page), or $0000-$00FF (immediate)? It sounds a bit kludgy to me, but I can't currently think of a better solution. Can someone else think of a preferable way to fix this issue in CA65? Best wishes, John |
|
From: <groepaz@gm...> - 2016-06-22 22:55:14
|
On Wednesday 22 June 2016, 23:43:17 John Brandwood <john@...> wrote: > Hi guys, > > I've been using CA65 for a while now, and it's finally time to bring > up the problem of zero-page addressing. > > On the HuC6280, the regular zero-page addressing modes actually refer > to locations $2000-$20FF. > > As such, it's more like the 6809's direct-page addressing ... but > limited to a single page. > > CA65 is assuming that all one-byte addressing modes refer to a > location in the range $0000-$00FF, i.e. the high byte is zero. > > I've been trying to fix this so that CA65 will correctly use the > direct-page/zero-page addressing for constant symbols in the range > $2000-$20FF, and could use some advice. > > It only looks like I have to modify CA65 in 3 locations to get this to work. > > 1) in studyexpr.c at approx line 449 when CA65 is deciding whether a > constant symbol should use 1 or 2 byte addressing. > > This looks like a safe change to make. > > 2) in segment.c at approx line 394 when CA65 is checking for range errors. > > Disabling this check for the HuC6280 seem like the only option. > > It doesn't look like this would to cause any problem, because the > range checking is done again anyway during code output in objcode.c > > 3) in objcode.c at approx line 71 when the assembler is emitting an > instruction with a 1 byte argument. > > This is where I have a question. > > I can disable the range check for the HuC6280, but then we'd lose the > safety of range checking on immediate instructions. > > There's no addressing-mode information being passed into Emit1(), so > the only thing that I've got to differentiate between a direct-page > reference and an immediate byte is the opcode. > > Does it sound OK to folks for me to put a 256-byte > opcode-to-addressing-mode table into objcode.c to see if the check > should be for $2000-$20FF (direct-page), or $0000-$00FF (immediate)? > > It sounds a bit kludgy to me, but I can't currently think of a better > solution. > > Can someone else think of a preferable way to fix this issue in CA65? not sure what you are trying to fix there.... when zp adressing is used, a 2 byte instructions should get generated. and if regular addressing is used, a 3 byte addressing is generated. - and what page the two byte addressing actually refers to entirely depends on how the MMU/MPR is set up, which the assembler doesnt know, and which even may change at runtime. -- http://www.hitmen-console.org http://magicdisk.untergrund.net http://www.pokefinder.org http://ar.pokefinder.org Real life ist für Loser, die keinen C64 haben! <Telespielator> |
|
From: <groepaz@gm...> - 2016-06-22 23:11:53
|
On Thursday 23 June 2016, 00:55:00 groepaz@... wrote: > and what page the two byte addressing > actually refers to entirely depends on how the MMU/MPR is set up ugh. i should have taken a break from debugging actual MMU code before writing this - what you said is true of course =) i still think it'd be weird if the assembler assembles "lda $2000" into a two byte instruction however - you should be writing "lda $00" instead and treat the first RAM page as actual zeropage, what speaks against it anyway? -- http://www.hitmen-console.org http://magicdisk.untergrund.net http://www.pokefinder.org http://ar.pokefinder.org No matter what the anticipated result, there will always be someone eager to (a) misinterpret it, (b) fake it, or (c) believe it happened according to his own pet theory. |
|
From: John Brandwood <john@te...> - 2016-06-23 18:29:32
|
> Re: [cc65-devel] HuC6280 zero-page addressing problem. > From: <groepaz@...> - 2016-06-22 23:11:53 > > On Thursday 23 June 2016, 00:55:00 groepaz@... wrote: > > and what page the two byte addressing > > actually refers to entirely depends on how the MMU/MPR is set up > > ugh. i should have taken a break from debugging actual MMU code before > writing this - what you said is true of course =) Hahaha, yes, you got things a bit mixed up there! > i still think it'd be weird if the assembler assembles "lda $2000" into a > two byte instruction however - you should be writing "lda $00" instead and > treat the first RAM page as actual zeropage, what speaks against it anyway? So you think that I should write $0000 when I really mean $2000??? That's a very strange suggestion, IMHO. Here's an example, of the problem in the real-world. There are specific locations that are used by Hudson's CD System Card BIOS. _ax = $20F8 _al = $20F8 _ah = $20F9 These are meant to be used with direct-page/zero-page addressing. So let's say that I do decide to lie just to make CA65 happy, and change those to ... _ax = $00F8 _al = $00F8 _ah = $00F9 What happens when I put the address of one of those variables into a pointer, or if I use an instruction that doesn't support 1-byte addressing? They'll end up pointing to the wrong locations! That's madness! Similarly, let's say that I want to write to location $0000, which is where the VDC chip is normally mapped. VDC_CTRL = $0000 VDC_DATA_LO = $0002 VDC_DATA_HI = $0003 Suddenly CA65 is generating a 1-byte addressing instruction and the write is actually going to $2000. Again, madness! The current solution for this is to add "<" or "a:" address overrides to every instruction in order to generate the proper code. That's ugly, and shouldn't be needed IMHO, but you guys may disagree. Isn't the point of computers that they should actually do things right? Don't you want CA65 to work in a way that matches the reality of the actual processor? If you expand your thinking of the concept of 1-byte addressing into something that's referring to the direct-page rather than just the zero-page, then you'll have no problem matching your current notions of what's correct to the actual reality of the situation. |
|
From: Greg King <gregdk@us...> - 2016-06-24 04:54:19
|
On 2016-06-23 2:29 PM, John Brandwood wrote: > Re: [cc65-devel] HuC6280 zero-page addressing problem. > From: groepaz -- 2016-06-22 23:11:53 >> >> I still think it'd be weird if the assembler assembles "lda $2000" into a >> two-byte instruction, however -- you should be writing "lda $00" instead; and, >> treat the first RAM page as actual zeropage; what speaks against it anyway? > > So, you think that I should write $0000 when I really mean $2000??? > That's a very strange suggestion, IMHO. > > Here's an example of the problem in the real-world. > There are specific locations that are used by Hudson's CD System Card BIOS. > > _ax = $20F8 > _al = $20F8 > _ah = $20F9 > > Those are meant to be used with direct-page/zero-page addressing. > So, let's say that I do decide to lie, just to make CA65 happy, and change > those to: > > _ax = $00F8 > _al = $00F8 > _ah = $00F9 > > What happens when I put the address of one of those variables into a pointer; > or, if I use an instruction that doesn't support 1-byte addressing? > They'll end up pointing to the wrong locations! That's madness! > > Similarly, let's say that I want to write to location $0000, which is where > the VDC chip normally is mapped. > > VDC_CTRL = $0000 > VDC_DATA_LO = $0002 > VDC_DATA_HI = $0003 > > Suddenly, CA65 is generating a 1-byte addressing instruction; and, the write > actually is going to $2000. Again, madness! > > The current solution for them is to add "<" or "a:" address-overrides to > every instruction, in order to generate the proper code. > That's ugly, and shouldn't be needed IMHO; but, you guys might disagree. The video chip problem is easy to fix: Use I/O mirror locations. VDC_CTRL := $0200 VDC_DATA := $0202 > > Isn't the point of computers that they actually should do things right? > Don't you want CA65 to work in a way that matches the reality of the > actual processor? > If you expand your thinking of the concept of 1-byte addressing into > something that's referring to the direct-page rather than just the > zero-page, then you'll have no problem matching your current notions > of what's correct to the actual reality of the situation. That issue is important for the Commodore 128 and the 65816 CPU also. My thought is a directive, similar to .ORG, that tells ca65 where the direct page currently is. When ca65 sees an address constant operand, it checks the mnemonic. If that instruction allows the direct-page addressing mode, and the constant is in the 256-byte range of the directive, then it is changed into a "zero-page" address. |
|
From: John Brandwood <john@te...> - 2016-06-25 18:07:27
|
Hi Greg, > Re: [cc65-devel] HuC6280 zero-page addressing problem. > From: Greg King <gregdk@...> - 2016-06-24 04:54:19 > > The video chip problem is easy to fix: Use I/O mirror locations. > VDC_CTRL := $0200 > VDC_DATA := $0202 You're right, that would work. But it's not something that I'd call a "fix", it's more of a "work-around", just like using an "a:" addressing override. > That issue is important for the Commodore 128 and the 65816 CPU also. My > thought is a directive, similar to .ORG, that tells ca65 where the > direct page currently is. When ca65 sees an address constant operand, > it checks the mnemonic. If that instruction allows the direct-page > addressing mode, and the constant is in the 256-byte range of the > directive, then it is changed into a "zero-page" address. IMHO, some form of direct-page setting like this could be the true "fix". A ".direct" or ".setdp" directive is, IIRC, the usual solution in 65816 assemblers. The details of how you you'd actually implement that in CA65 would be the key. The obvious question is ... would you allow the programmer to set the direct-page to a symbol whose location is not resolved until link-time? > ....................... If that instruction allows the direct-page > addressing mode, and the constant is in the 256-byte range of the > directive, then it is changed into a "zero-page" address. This is where I wasn't comfortable in hacking into CA65. The test to see whether you can use 1-byte-addressing is done in studyexpr.c at approx line 449, where I've put my current modification. The question is ... what do you do then? My code leaves the constant symbol unchanged (i.e. $2000-$20FF), and that means that segment.c and objcode.c have to be modified to understand the idea that a 1-byte-addressing constant does't always have zero high byte. Are you suggesting that it would be a "better" fix to mangle the actual expression value to clear the high-byte to zero in studyexpr.c whenever 1-byte-addressing is going to be used? If so, that would answer my original question of "is there a better way?". I can certainly try it, and see if it breaks things. Best wishes, John |
|
From: Christian Groessler <chris@gr...> - 2016-06-25 22:03:43
|
On 06/25/16 20:07, John Brandwood wrote: > The obvious question is ... would you allow the programmer to set the > direct-page to a symbol whose location is not resolved until link-time? Definitely yes, but I'd guess the linker would have to be changed for that, too. But Greg's proposal is the way to go, IMO. regards, chris |
|
From: John Brandwood <john@te...> - 2016-06-26 22:04:03
|
> From: John Brandwood <john@...>
>
> Are you suggesting that it would be a "better" fix to mangle the actual
> expression value to clear the high-byte to zero in studyexpr.c whenever
> 1-byte-addressing is going to be used?
>
> If so, that would answer my original question of "is there a better way?".
>
> I can certainly try it, and see if it breaks things.
Well, I tried this, and it was a horrible mess and broke a bunch of stuff.
But looking closer at the code, and thinking about how a comprehensive
direct-page patch might look in the future, lead me to realize that my
previous attempt at a patch was acting at too low a level.
IMHO, the proper solution is to change the code in instr.c in the EmitCode()
function, and to split Emit1() into 2 different calls, the old Emit1() that
gets used for opcodes with and immediate or relative operand, and a new
EmitZP() call that gets used to output a zero-page/direct-page opcode.
The test for which function to use is as simple as ...
if (A->AddrModeBit & (AM65_ALL_ZP |
AM65_DIR_IND_Y | AM65_DIR_IND_LONG | AM65_DIR_IND_LONG_Y)) {
EmitZP (A->Opcode, A->Expr);
} else {
Emit1 (A->Opcode, A->Expr);
}
It would be even cleaner if the AM65_ALL_ZP define had included all of the
zero-page addressing modes like it claims to do.
Does anyone have an idea why it doesn't?
It looks like the define is only used once in the codebase, and that in the
place where it is used, it almost certainly should be including the extra 3
addressing modes that I had to add.
Could it be a bug?
Anyway, my new patch is simpler, cleaner, limited, and works, so I'll submit
an Issue and a Pull Request on gihub.
Best wishes,
John
|
|
From: John Brandwood <john@te...> - 2016-07-02 17:31:38
|
On Sun, Jun 26, 2016 at 11:03 PM, John Brandwood <john@...> wrote: > Anyway, my new patch is simpler, cleaner, limited, and works, so I'll submit > an Issue and a Pull Request on gihub. Well, I was wrong, the patch doesn't always work. It breaks when used inside "proc" sections because of the symbol scoping and the way in which CA65 tries to figure out what effective-addressing mode to use. Looking at the code path for determining the effective address has also answered my previous question ... > It would be even cleaner if the AM65_ALL_ZP define had included all of the > zero-page addressing modes like it claims to do. > > Does anyone have an idea why it doesn't? > > It looks like the define is only used once in the codebase, and that in the > place where it is used, it almost certainly should be including the extra 3 > addressing modes that I had to add. > > Could it be a bug? The answer, IMHO, is "yes" it really is a bug. The section of EvalEA() code that uses AM65_ALL_ZP exists to provide a safe fall-back for determining the size of an EA when it cannot otherwise be figured out. As such, AM65_ALL_ZP really does need to include the extra 3 defines in order to work properly. The problem for my patch is that the expression "symbol + constant" is looking at the size of the constant to determine whether an EA is 1-byte or 2-bytes. But, if the symbol is already defined, then it throws away the answer and just looks at the information from the symbol itself (i.e. importzp symbols imply that an EA should always use a ZP instruction if possible). Now, the reason that my patch works in the top scope, but fails within "proc" scope is because the code that attempts to guess the EA is following different paths, and doing some very strange stuff, because the code is inconsistant in whether it attempts to resolve symbols in the parent scope. In one place it does, and in another place it doesn't, and if is does, then it doesn't really believe the answer that it's got anyway. Because the EA code doesn't really believe that it has found the symbol if it is in parent scope, then the override code doesn't get invoked, and the EA calculation makes the wrong guess if the high-bye of the constant is not zero. Is there someone here that really understands EvalEA() and how it interacts with StudyExpr(), and why things got to be as complicated as they are now? It looks to me like the rules should be pretty simple, and that things are a bit broken in there right now, but perhaps I'm missing something important? Best wishes, John |
|
From: Greg King <gregdk@us...> - 2016-07-06 04:48:49
|
On 2016-06-25 2:07 PM, John Brandwood wrote: > > The obvious question is ... would you allow the programmer to set the > direct-page to a symbol whose location is not resolved until link-time? ld65 knows nothing about op-codes; therefore, it cannot change instructions. .DPAGE's operand will need to be an assembler constant. |
|
From: Damian Yerrick <tepples@gm...> - 2016-07-25 21:40:41
|
On Wed, Jul 6, 2016 at 12:48 AM, Greg King <gregdk@...> wrote: > > On 2016-06-25 2:07 PM, John Brandwood wrote: > > > > The obvious question is ... would you allow the programmer to set the > > direct-page to a symbol whose location is not resolved until link-time? > > ld65 knows nothing about op-codes; therefore, it cannot change > instructions. .DPAGE's operand will need to be an assembler constant. A non-constant argument to .DPAGE can still be useful in theory if the *difference* between each affected operand and the .DPAGE operand is constant. Or if the program forces direct page, such as through a z: prefix or a direct-page-only addresing mode (such as (d),Y), it can assemble the instruction and add a link-time assertion that the operand is 0-255. .p816 .import someabsvar, another quarterin = someabsvar + $40 halfwayin = someabsvar + $80 pea someabsvar pld .dpage someabsvar ; These should assemble to DP opcodes because ; (quarterin - someabsvar) is constant at assembly time lda z:quarterin lda (quarterin),y lda quarterin ; These should assemble to DP opcodes followed by ; an assertion that (another - someabsvar) is in 0-255 lda z:another lda (another),y ; This should assemble to an absolute opcode because ; the difference between the symbols can't be determined ; at assembly time lda another |
|
From: Greg King <gregdk@us...> - 2016-07-06 05:54:07
|
On 2016-07-02 1:31 PM, John Brandwood wrote:
>
> Looking at the code path for determining the effective address has also
> answered my previous question.
>
>> It would be even cleaner if the AM65_ALL_ZP define had included all of the
>> zero-page addressing modes, as it claims to do.
>>
>> Does anyone have an idea why it doesn't?
>>
>> It looks like the define is used only once in the codebase; and that, in the
>> place where it is used, it almost certainly should be including the extra 3
>> addressing modes that I had to add.
>> Could it be a bug?
>
> The answer, IMHO, is "yes"; it really is a bug.
> The section of EvalEA() code that uses AM65_ALL_ZP exists to provide a
> safe fall-back for determining the size of an EA when it cannot otherwise
> be figured out.
> As such, AM65_ALL_ZP really does need to include the extra 3 defines, in order
> to work properly.
I agree. It looks like a copy-and-paste mistake. Uz started it by
copying AM65_SET_ZP; but then, he forgot to finish it.
>
> The problem for my patch is that the expression "symbol + constant" is looking
> at the size of the constant to determine whether an EA is 1-byte or 2-bytes.
>
> But, if the symbol is already defined, then it throws away the answer, and
> just looks at the information from the symbol itself (i.e., importzp symbols
> imply that an EA should always use a ZP instruction if possible).
>
> Now, the reason that my patch works in the top scope, but fails within "proc"
> scope is because the code that attempts to guess the EA is following different
> paths; and, doing some very strange stuff because the code is inconsistant in
> whether it attempts to resolve symbols in the parent scope.
> In one place, it does; and, in another place, it doesn't; and if it does, then
> it doesn't really believe the answer that it's got anyway.
>
> Because the EA code doesn't really believe that it has found the symbol if
> it is in parent scope, then the override code doesn't get invoked; and, the
> EA calculation makes the wrong guess if the high-byte of the constant is
> not zero.
>
> Is there someone here that really understands EvalEA() and how it interacts
> with StudyExpr(), and why things got to be as complicated as they are now?
> It looks to me like the rules should be pretty simple; and, that things are a
> bit broken in there right now; but, perhaps I'm missing something important?
I think that it simply is the result of expression promotion. ca65
promotes them in the same way that C compilers do it. When ca65 sees a
line like this:
lda (regbank + 4),y
it knows that "regbank" is 1 byte (because of .importzp). Then, it uses
GetConstAddrSize() to learn what is the width of the "4" (yes, its name
is misleading; it should have been called "GetConstDataWidth"). Your
previous patch said that the "4" is 2 bytes. Therefore, the result of
that addition must be promoted to 2 bytes -- which is illegal in that
instruction.
Also, take a look at "libsrc/zlib/inflatemem.s"; it has four
instructions that were rejected by your previous patch in EmitZP()
(lines 142, 167, 173, and 175).
My conclusion is that they are the wrong functions for what you want to
do (but, I haven't found the better places).
|
|
From: John Brandwood <john@te...> - 2016-07-07 17:03:00
|
> From: Greg King <gregdk@...> - 2016-07-06 05:54:07 > > I think that it simply is the result of expression promotion. ca65 > promotes them in the same way that C compilers do it. When ca65 sees a > line like this: > > lda (regbank + 4),y > > it knows that "regbank" is 1 byte (because of .importzp). Then, it uses > GetConstAddrSize() to learn what is the width of the "4" (yes, its name > is misleading; it should have been called "GetConstDataWidth"). Your > previous patch said that the "4" is 2 bytes. Therefore, the result of > that addition must be promoted to 2 bytes -- which is illegal in that > instruction. Yes, that is why the previous patch was failing ... but AFAIK only on instructions within "proc" scope because regbank is defined in global scope. IMHO, it is a fundamental flaw with CA65's processing that a constant that is used as an offset within an expression is being evaluated in isolation in order to determine if the whole expression refers to a zero-page location or not. IMHO, the only reasonable way to determine that is to look at the final result of the expression, and not its components. If you look at StudyExpr(), you'll see that Ulrich apparently came to the same conclusion, because there is override code in there to throw away the result of that flawed process and to recalculate it based upon the location of symbols alone, or the final result of a constant expression. Unfortunately, that code isn't getting consistently used when in "proc" scope, because symbols (including .importzp symbols) that are defined in global scope are treated as untrustworthy. I'm not even sure why all this calculation is being done all the time inside StudyExpr() instead of being in EvalEA() where it would seem to me to naturally belong. > My conclusion is that they are the wrong functions for what you want to > do (but, I haven't found the better places). I'll be interested to hear if you can find a better place. I'd suggest moving the decision into EvalEA(), but you'd need to make some pretty major changes to the expression evaluation code when in proc scope in order to have it actually use and trust the values of symbols that are defined in global scope. That seems to make a lot of sense to me ... but that's not how it is being done now, and you guys may know of some reason why that decision was made. |
|
From: Oliver Schmidt <ol.sc@we...> - 2016-07-08 07:53:12
|
Hi John, [...] That seems to make a lot of sense to me ... but that's not how it is > being > done now, and you guys may know of some reason why that decision was made. > At least I for sure don't know. According to my memory Uz about never discussed internal design aspects of the tool chain publicly. Regards, Oliver |
|
From: John Brandwood <john@te...> - 2016-10-30 19:50:41
|
On Thu, Jul 7, 2016 at 6:02 PM, John Brandwood <john@...> wrote: >> From: Greg King <gregdk@...> - 2016-07-06 05:54:07 >> >> I think that it simply is the result of expression promotion. ca65 >> promotes them in the same way that C compilers do it. When ca65 sees a >> line like this: >> >> lda (regbank + 4),y >> >> it knows that "regbank" is 1 byte (because of .importzp). Then, it uses >> GetConstAddrSize() to learn what is the width of the "4" (yes, its name >> is misleading; it should have been called "GetConstDataWidth"). Your >> previous patch said that the "4" is 2 bytes. Therefore, the result of >> that addition must be promoted to 2 bytes -- which is illegal in that >> instruction. Hi Greg ... I can see that you've made some more changes to your personal branch for the new ".setdp" instruction since I last tested it out ... but those changes still haven't been applied to the CC65 master branch. May I ask how things are going, and if there's anything that I can do to help get that new functionality adding into the main branch? |
|
From: John Brandwood <john@te...> - 2016-10-30 21:01:36
|
On Sun, Oct 30, 2016 at 7:19 PM, John Brandwood <john@...> wrote: > > Hi Greg ... I can see that you've made some more changes to your > personal branch for the new ".setdp" instruction since I last tested > it out ... but those changes still haven't been applied to the CC65 > master branch. > > May I ask how things are going, and if there's anything that I can do > to help get that new functionality adding into the main branch? Well, a build of Greg's latest source shows that some great progress has been made! It looks like the automatic ".setdp $2000" that's being done for the HuC6280 processor is working wonderfully ... at least in the normal top-level scope. But things are totally hosed within a "proc" scope. So, a giant step forward, and totally usable from my POV as an assembly-language programmer that doesn't use/need CA65's scoping capability. IMHO, the next step toward finally nailing this thing is probably going to be some work on CA65's symbol-table code to make those scopes work better (which would probably also fix issue #235 at the same time). What are your thoughts Greg, now that you've dug into the source code and got the ".setdp" working? |