I cannot get the following to work in my emulator even if I follow the following guide VERY accurately;
http://www.ataripreservation.org/websit ... lopc31.txtARR
ATX
AXS
DCP
ISC
RRA
SXA
SYA
Can somebody please check the above document and tell me if it is accurate or not and if not please provide any corrections. All of the CPU test ROMs that I have tried give error codes that don't tell you what is actually wrong with your emulation.
Any unofficial opcode not listed on
this page probably need not be implemented.
ARR is AND #imm ROR A, with different flags.
ATX varies based on ambient temperature and RF and is discouraged. Probably nothing but test ROMs uses it.
AXS is X = (A AND X) - imm
DCP is DEC addr then CMP addr
ISC is INC addr then SBC addr
RRA is ROR addr then ADC addr
SXA, also called SHX, stores X AND ((address >> 8) + 1) at the address. Probably nothing but test ROMs uses it.
SYA, also called SHY, stores Y AND ((address >> 8) + 1) at the address. Probably nothing but test ROMs uses it.
I've fixed AXS now. Even if no game actually uses it, I still want to emulate it. Test ROMs will fail if I don't and its still NES behaviour.
The problem is, when to set the flags? For instance, RRA fetches the byte from memory, rotates it right 1 bit and then performs ADC on it setting the flags like ADC normally does. Yet it still fails. Are any of the flags only modified during that rotate right?
WedNESday wrote:
I've fixed AXS now. Even if no game actually uses it, I still want to emulate it.
I plan to use it for sprites.
Quote:
The problem is, when to set the flags? For instance, RRA fetches the byte from memory, rotates it right 1 bit and then performs ADC on it setting the flags like ADC normally does. Yet it still fails. Are any of the flags only modified during that rotate right?
N and Z come out of the ROR, but C and V come out of the odd mixture of ADC and AND.
AXS is a great instruction.
Code:
; move all sprites offscreen
ldx #$00
lda #$FF
loop:
sta $0200, x
axs #<-$04
bne loop
Movax12 wrote:
AXS is a great instruction.
Code:
; move all sprites offscreen
ldx #$00
lda #$FF
loop:
sta $0200, x
axs #<-$04
bne loop
That's a really nice trick, I'm going to have to steal that one.
tepples wrote:
N and Z come out of the ROR, but C and V come out of the odd mixture of ADC and AND.
And I have done what the document in my first post does exactly and still no luck.
Code:
A &= DataBus;
N = Z = A >>= 1;
switch (A & 0x60)
{
case 0x00:
V = 0x00;
C = 0x00;
break;
case 0x20:
V = 0x40;
C = 0x00;
break;
case 0x40:
V = 0x40;
C = 0x01;
break;
case 0x60:
V = 0x00;
C = 0x01;
break;
}
What gets me is that that is the only document on the internet which actually addresses 6502 illegal opcodes. None of the browser emulators I could find emulated any of the illegal opcodes.
I'm pretty sure Visual 6502 does the unofficial opcodes, given that it's a transistor-level simulation of the original silicon.
tepples wrote:
I'm pretty sure Visual 6502 does the unofficial opcodes, given that it's a transistor-level simulation of the original silicon.
According to Visual 6502, C is the lowest bit of A before the shift right which is not what that other document says. Also Visual 6502 doesn't set the V flag during the operation. So now we have 2 conflicting statements.
ARR = AND + ROR as opposed to AND + LSR as I had been treating it. Thanks to Nestopia's source code for the fix.
ATX, according to the document says AND A with Immediate and transfer to X. Nestopia says load A and X with immediate value. blargg's test ROM agrees with Nestopia both times.
ATX is one of the instructions in the analog feedback zone, although comparatively well behaved. It is the intersection of TAX and LDA #immed, so it's something like:
Simultanously:
* Drive SB with value from RAM/ROM
* Drive SB with value of A
* Load A with value from SB
* Load X with value from SB
The first two have an AND effect, that's definitely correct. The 2nd and 3rd are the analog feedback trap; like in XAA it means that the six bits of $EE will all act the same, and two bits of $11 will all act the same, but both groups may not act the same.
I'd bet it'll do something wonky if a DPCM read intersects with it.
Yeah, I wondered if some opcodes would vary from machine to machine or be analogue.
ISC
Code:
DataBus++;
DataBus ^= 0xFF;
ADC();
For some reason the above code fails on all test ROMs that I have tried. Even though ADC works for certain and inverting the DataBus beforehand to emulate SBC also works.
ISC means you do the whole INC before the subtraction, including writeback.
tepples wrote:
ISC means you do the whole INC before the subtraction, including writeback.
Writeback? If you mean the write to memory that is done, I merely omitted it above.
Only need to do SXA and SYA now. According to Nestopia's source code, you AND X with the high byte of the target address + 1 and this sets the address rather than the value of the databus itself.
Address = (X & (High + 1));
and not
Databus = (X & (High + 1));
Is this correct?
Has the NES scene decided upon a consistent and pseudo-official (say, NESdev official) set of names for all of the opcodes yet?
Last time I looked, there were 4-5 variants for each unofficial opcode.
If not, I'd strongly recommend doing so. I don't care what they are (so long as there are no overlaps; such that any can be specified distinctly with an assembler), I'd just like everyone to have the same name for each one.
byuu wrote:
Has the NES scene decided upon a consistent and pseudo-official (say, NESdev official) set of names for all of the opcodes yet?
In the wiki's pages about
the opcode matrix and
uses of the stable unofficial opcodes, I used the names from the
6502X section of the ca65 Users Guide, which got them from
Graham of Oxyron. I used the same mnemonics in
my reimplementation of 6502 opcodes as ca65 macros.
Another document you should be aware of is
http://www.ffd2.com/fridge/docs/6502-NMOS.extra.opcodes . I used that one along with the one you linked when I implemented the unofficial opcodes. IIRC, the 6502-NMOS.extra.opcodes version tends to be correct for instructions where they differ.
The instruction logic in nesalizer should be pretty easy to read if you don't mind looking at some code. Search for "Core instruction logic" in
https://github.com/ulfalizer/nesalizer/ ... er/cpu.cpp . It passes Kevtris' and blargg's unofficial instruction test ROMs.
And yeah, it would be nice there were agreed-upon mnemonics. I probably have a mix of them from different sources.
Just tested Visual6502 and SXA, 9E doesn't increment that high byte of the argument by 1.
Visual6502
DataBus = (X & ((temp >> 8) + 0));
What we are told
DataBus = (X & ((temp >> 8) + 1));
The behavior of 9C and 9E depends on whether you get a page crossing or not, see
viewtopic.php?f=3&t=3831&start=30#p113343 . Should probably add to the wiki.
I just threw this in visual 6502:
Code:
LDX #$FF
LDY #$80
.byte $9E, $80, 0
And it stored 1 to $0100, and took 5 cycles.
I then threw this is visual 6502:
Code:
LDX #$FF
LDY #$40
.byte $9E, $80, 0
And it stored 1 to $00C0, and took 5 cycles.
So ... dunno??
lidnariq wrote:
I just threw this in visual 6502:
Code:
LDX #$FF
LDY #$80
.byte $9E, $80, 0
And it stored 1 to $0100, and took 5 cycles.
I then threw this is visual 6502:
Code:
LDX #$FF
LDY #$40
.byte $9E, $80, 0
And it stored 1 to $00C0, and took 5 cycles.
So ... dunno??
Looks correct. Both the value (of X in this case)
and the high byte of the target address are corrupted when there's a page crossing. When there's no page crossing, only the value is corrupted.
What I think is going is that the CPU always calculates the high address byte + 1 in case it's needed and puts it on bus B (arbitrary name). Due to a bus conflict, the value (of X in this case) is also put on bus B, which gives the &. When there's a page crossing, the CPU also tries to get the corrected high address byte from bus B, and so gets the same corrupted value there.
Edit: In this case I guess the target address is never corrupted though, since X if $FF.
WedNESday wrote:
Just tested Visual6502 and SXA, 9E doesn't increment that high byte of the argument by 1.
Visual6502
DataBus = (X & ((temp >> 8) + 0));
What we are told
DataBus = (X & ((temp >> 8) + 1));
What's
temp there? If it's the target address (after the high address byte has been corrected), then it's what I'd expect if there's a page crossing.
temp = low + high + X or Y;
ulfalizer wrote:
Edit: In this case I guess the target address is never corrupted though, since X if $FF.
Ahah.
X=$FD, Y=$F0,
.byte $9e, $f0, $02 ultimately writes 1 to $01e0, instead of writing 3 to $03e0.
And replacing Y with $0 writes 1 to $02f0
SXA now passes all test ROMs.
Code:
if (low + Y > 0xFF)
low += (((high & X) << 8) + Y);
else
low += ((high << 8) + Y);
DataBus = (X & (high + 1));
Thanks for the help guys.
WedNESday wrote:
address = (((high & X) << 8) + Y) + low;
That looks wrong. Shouldn't it be
address = (((high+1)&X) << 8) + ((Y+low)&255)?
For example, if the sequence were
X=$FD, Y=$F0,
.byte $9e, $f0, $01, what you have would write 0 to $02e0, but it should write 0 to $00e0.
Sorry for the bump, but I can't figure out the SHY/SHX ($9C,$9E) opcode timing diagram AND operation.
Could someone make it crystal clear, plz? ^_^;;
Zepper wrote:
Sorry for the bump, but I can't figure out the SHY/SHX ($9C,$9E) opcode timing diagram AND operation.
Could someone make it crystal clear, plz? ^_^;;
Code:
// 1
opcode = read(PC);
incrementPC();
// ...
private void ABSOLUTE_INDEXED_WRITE() {
// 2
int value = read(PC);
incrementPC();
// 3
final int offset;
value |= read(PC) << 8;
switch(opcode) {
case 0x9C: // *SHY $A5B6,X
case 0x9D: // STA $A5B6,X
offset = X;
break;
default:
offset = Y;
break;
}
int address = (value + offset) & 0xFFFF;
value = (value & 0xFF00) | (address & 0x00FF);
incrementPC();
// 4
read(value);
// 5
switch(opcode) {
case 0x99: // STA $A5B6,Y
case 0x9D: // STA $A5B6,X
write(address, A);
break;
case 0x9C: // *SHY $A5B6,X
if ((value >> 8) != (address >> 8)) {
value &= Y << 8;
}
write(value, Y & ((value >> 8) + 1));
break;
case 0x9E: // *SHX $A5B6,Y
if ((value >> 8) != (address >> 8)) {
value &= X << 8;
}
write(value, X & ((value >> 8) + 1));
break;
case 0x9F: // *SHA $A5B6,Y
if ((value >> 8) != (address >> 8)) {
value &= (X & A) << 8;
}
write(address, X & A & ((address >> 8) + 1));
break;
}
}