Skip to content

Commit f0b3a46

Browse files
committed
asm output: fix more NES zero-page edge cases
- to preserve byte-identical output lengths
1 parent d807727 commit f0b3a46

1 file changed

Lines changed: 52 additions & 4 deletions

File tree

Diz.Cpu.65816/src/CPU65C816.cs

Lines changed: 52 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -325,12 +325,60 @@ public override CpuInstructionDataFormatted GetInstructionData(TByteSource data,
325325
{
326326
var intermediateAddress = data.GetIntermediateAddress(offset, resolve: true);
327327

328-
// hack 1: force non-zeropage optimization to preserve byte-identical output
328+
var isZeroPageAddr = intermediateAddress is >= 0 and <= 0xFF;
329+
330+
var shouldForceZeroPage =
331+
!operandIsNumeric && // not 100% sure we should check operandIsNumeric. remove to spam z: a little more but is maybe safer.
332+
isZeroPageAddr &&
333+
mode is Cpu65C816Constants.AddressMode.DirectPage
334+
or Cpu65C816Constants.AddressMode.DirectPageXIndex
335+
or Cpu65C816Constants.AddressMode.DirectPageYIndex
336+
or Cpu65C816Constants.AddressMode.DirectPageIndirect
337+
or Cpu65C816Constants.AddressMode.DirectPageXIndexIndirect
338+
or Cpu65C816Constants.AddressMode.DirectPageIndirectYIndex;
339+
340+
var shouldForceAbsolute =
341+
isZeroPageAddr &&
342+
mode is Cpu65C816Constants.AddressMode.Address
343+
or Cpu65C816Constants.AddressMode.AddressXIndex
344+
or Cpu65C816Constants.AddressMode.AddressYIndex;
345+
346+
// force working around non-zeropage optimization to preserve byte-identical output
329347
// (note: not sure how this interacts in all cases with the other mapping hack below, may require better integration with the two)
330-
if (intermediateAddress is >= 0 and <= 0xFF && mode is Cpu65C816Constants.AddressMode.Address or Cpu65C816Constants.AddressMode.AddressXIndex or Cpu65C816Constants.AddressMode.AddressYIndex)
331-
{
348+
if (shouldForceZeroPage) {
349+
// explanation:
350+
// take these bytes from the ROM:
351+
// 85 A7
352+
// that means:
353+
// STA $A7
354+
// if we replace A7 with a label:
355+
// my_var = $0000A7
356+
// or
357+
// my_var = $A7
358+
// and do this:
359+
// STA my_var
360+
// ca65 will not know it's being accessed via the zeropage and will use two bytes as the output:
361+
// 85 00 A7 *I think. double check.
362+
// that's functionally identical code (even if a bit slower), but, it breaks byte-identical compatability with the output (which we care about for ROM disassemblies).
363+
// what we need to do is tell it, no, this is OK to use as a zeropage, and only use one byte. we can prepend "z:" to force that to output as 1 byte:
364+
// STA z:my_var
365+
operandFinalStr1 = $"z:{operandFinalStr1}";
366+
} else if (shouldForceAbsolute) {
332367
// prefix with "a:" to prevent the assembler from auto-optimizing this to 1 byte
333-
// it's wasteful but we're going for whatever the original ROM was doing
368+
// it's TECHNICALLY wasteful and a problem with the original ROM code, BUT we're going to replicate it for the sake of byte-identical output.
369+
//
370+
// explanation: if the original instruction bytes looked like this:
371+
// B9 44 00
372+
// that means:
373+
// LDA $0044,Y
374+
// but TECHNICALLY you don't need the leading zeroes to access $44. so ca65 parses LDA and figures that out and emits THIS instead (which is shorter):
375+
// B9 44
376+
// which is functionally identical (and faster at runtime).
377+
//
378+
// but, for us it's bad because it breaks byte-identical compatability with the original ROM.
379+
// so instead, what we do is emit this directive in ca65:
380+
// LDA a:$0044,Y
381+
// that "a:" prefix forces it to use $0044 and NOT optimize it to just $44, which is what WE want.
334382
operandFinalStr1 = $"a:{operandFinalStr1}";
335383
}
336384

0 commit comments

Comments
 (0)