6502.org Forum  Projects  Code  Documents  Tools  Forum
It is currently Sun Nov 24, 2024 9:34 pm

All times are UTC




Post new topic Reply to topic  [ 11 posts ] 
Author Message
PostPosted: Thu May 14, 2020 11:32 pm 
Offline

Joined: Fri Nov 16, 2018 8:55 pm
Posts: 71
While disassembling a C64 game cartridge I found a number of undocumented op codes being used. Functional illegal opcodes sometimes make sense, if they do something useful and save a few bytes/cycles when you need them the most.

But, I have a bunch of multi-byte nops:

Code:
$1a          ; nop
$3a          ; nop
$80 $49      ; nop #49
$40 $00      ; nop $00
$fc $fefe    ; nop $fefe


Presumably, these don't do anything and the operands are discarded. Why a 2 and 3 byte nop? I presume this is the same as nop nop (two bytes) and nop, nop, nop (3 bytes) correct?

But, can someone give me some idea why this might have been done? Would it have purely to make disassembly harder in an era before "undocumented" op codes were, well, completely documented?


Top
 Profile  
Reply with quote  
PostPosted: Thu May 14, 2020 11:44 pm 
Offline
User avatar

Joined: Fri Dec 12, 2008 10:40 pm
Posts: 1007
Location: Canada
The 6502 is a minimalist design. I think these undocumented NOPs are unintentional artifacts of that design.

But I'm just guessing.

_________________
Bill


Top
 Profile  
Reply with quote  
PostPosted: Fri May 15, 2020 12:33 am 
Offline
User avatar

Joined: Fri Dec 11, 2009 3:50 pm
Posts: 3367
Location: Ontario, Canada
The topic title is easy to misread. Yes, it's unintentional that the undocumented NOP op codes exist -- that the silicon behaves that way. I think load81 is asking why the undocumented NOP op codes sometimes get used.

Perhaps in some cases it's the best way to waste a certain number of cycles and thus create a specific delay. Note that the official NOP, $EA, won't allow you to waste an odd number of cycles.

I have some other suggestions, although I admit I'm just guessing. Maybe there are cases where the code self-modifies, and the "NOP" opcode of a multi-byte instruction gets conditionally replaced by a real opcode??

-- Jeff

_________________
In 1988 my 65C02 got six new registers and 44 new full-speed instructions!
https://laughtonelectronics.com/Arcana/ ... mmary.html


Top
 Profile  
Reply with quote  
PostPosted: Fri May 15, 2020 12:54 am 
Offline

Joined: Fri Apr 15, 2016 1:03 am
Posts: 140
Are you sure about your disassembler? $40 is RTI, a 1 byte opcode valid on all 6502 versions.

Are you trying to disassemble data? The game cartridge ROM can contain other data interleaved with 6502 opcode sequences.

These are all valid 65c02 opcodes. But I assume stock C64 machines have an NMOS 6502.


Top
 Profile  
Reply with quote  
PostPosted: Fri May 15, 2020 1:46 am 
Offline

Joined: Fri Nov 16, 2018 8:55 pm
Posts: 71
Dr Jefyll wrote:
The topic title is easy to misread. Yes, it's unintentional that the undocumented NOP op codes exist -- that the silicon behaves that way. I think load81 is asking why the undocumented NOP op codes sometimes get used.


That's right. Now that I look at it with fresh eyes, the title of my post is pretty ambiguous. I am curious why these things get used practice. I'm aware they're an artifact of the original NMOS design process.

Dr Jefyll wrote:
Perhaps in some cases it's the best way to waste a certain number of cycles and thus create a specific delay. Note that the official NOP, $EA, won't allow you to waste an odd number of cycles.


Right, that's the most common use for a nop to create a delay for a predetermined number of cycles. I hadn't realized that nop ($ea) would only allow an even number of cycles to be burned off. I had it in my head (obviously incorrectly) that it burned one byte/cycle. Now you're making me go look stuff up! :)


Top
 Profile  
Reply with quote  
PostPosted: Fri May 15, 2020 2:12 am 
Offline

Joined: Fri Nov 16, 2018 8:55 pm
Posts: 71
leepivonka wrote:
Are you sure about your disassembler? $40 is RTI, a 1 byte opcode valid on all 6502 versions.


Ugh, damned dyslexia. :roll:

I'm not sure which exact line I pulled that from, but looking over my source code listing again it's virtually certain I transposed a nibble in my post. I believe the correct op code and operand are nop $00 ($04 $00).

That said, no, I'm not 100% sure of my disassembler. I'm using radare2 for my project. I was doing this all by hand one byte at a time, but after 1000 bytes I got sick of doing this by hand and switched to radare2 like so:

Code:
rasm2 -a 6502 -D -B -o 0x8000 -f no_header_game.img > game.asm


Then I used a bunch of vim macros to juggle text columns around and ensure things are formatted the way 64tass expects. I'm doing a manual pass right now to ensure everything lines up nicely and (hopefully!) assembles as expected when I'm done cleaning it up.

leepivonka wrote:
Are you trying to disassemble data? The game cartridge ROM can contain other data interleaved with 6502 opcode sequences.


That was a problem at first, even when I was doing this 100% by hand. Eventually, everything was just such nonsense that I knew I had screwed up somewhere along the line. After more than a few frustrated curses, I figured out that the first 89 bytes were the cartridge header and accompanying "chip packets." So, I used dd to strip off the cartridge header roughly as follows:

Code:
dd if=game.cart of=no_header_game.img bs=1 skip=89


When I do that, the first byte corresponds to the sei instruction as I expect.

Yes, stock C64's have NMOS 6502's. Those outfit with older CPU accelerators may have a CMOS variant. These days, accelerators like the Turbo Chameleon are fancy FPGA's that implement the "undocumented" op codes of the original NMOS variant.

It's my expectation that, now that I'm passed the header, I'm not going to run into any more data bytes that could be mistaken for code. Is that likely, or am I mistaken?

Assuming I'm wrong or to help me when I "graduate" to a disk-based game, what are some efficient ways to tell that I've hit data instead of instructions? Obviously, instructions that make zero sense is one way. But, I'm sure there are others.


Top
 Profile  
Reply with quote  
PostPosted: Fri May 15, 2020 3:16 am 
Offline

Joined: Mon May 21, 2018 8:09 pm
Posts: 1462
The usual approach is to start at a known entry point, then follow the code to see what parts are reachable by execution. In a bootable ROM, you start with the Reset, NMI and IRQ vectors which can be assumed to point to an instruction. In a cartridge or disk file, some part of the header (or file metadata) will indicate an execution entry point.


Top
 Profile  
Reply with quote  
PostPosted: Fri May 15, 2020 8:05 pm 
Offline

Joined: Fri May 05, 2017 9:27 pm
Posts: 895
leepivonka wrote:
These are all valid 65c02 opcodes. But I assume stock C64 machines have an NMOS 6502.

Actually, it's a 6510 processor, but yes it has the same instruction set as the NMOS 6502.


Top
 Profile  
Reply with quote  
PostPosted: Fri May 15, 2020 9:09 pm 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8546
Location: Southern California
JimBoyd wrote:
Actually, it's a 6510 processor

which is a 6502 with an I/O port tacked on.

_________________
http://WilsonMinesCo.com/ lots of 6502 resources
The "second front page" is http://wilsonminesco.com/links.html .
What's an additional VIA among friends, anyhow?


Top
 Profile  
Reply with quote  
PostPosted: Sat May 16, 2020 12:58 am 
Offline

Joined: Fri May 05, 2017 9:27 pm
Posts: 895
GARTHWILSON wrote:
JimBoyd wrote:
Actually, it's a 6510 processor

which is a 6502 with an I/O port tacked on.

I wish it had been a 65C02 with an I/O port tacked on! Oh well.


Top
 Profile  
Reply with quote  
PostPosted: Fri Jun 12, 2020 2:28 pm 
Offline

Joined: Fri Nov 16, 2018 8:55 pm
Posts: 71
I just wanted to update this thread to let everyone know the true origin of the weird nop instructions. They're not instructions, they're clearly data. The clue that tipped me off was the presence of bytes that got decoded as branch instructions. When I followed the branch instructions in that area of memory, they had a tendency to land on operand bytes not instruction bytes.

The only way this works is if they're not instructions at all but data.

After looking at it for a bit the approximate boundary between real instructions and data bytes became reasonably clear. For now, I have taken the entire area I believe to be data and converted them to .byte assembler ops. Obviously, I'll have to circle back at some point and figure out the specifics.

As an added bonus, there are no more undocumented MOS 6502 instructions in the code-base now. That will make the code easier to port if I ever decide to do so.


Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 11 posts ] 

All times are UTC


Who is online

Users browsing this forum: Google [Bot] and 4 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to: