6502.org Forum  Projects  Code  Documents  Tools  Forum
It is currently Thu Nov 21, 2024 2:38 pm

All times are UTC




Post new topic Reply to topic  [ 32 posts ]  Go to page 1, 2, 3  Next
Author Message
PostPosted: Sun Aug 15, 2010 5:14 pm 
Offline
User avatar

Joined: Thu Dec 11, 2008 1:28 pm
Posts: 10985
Location: England
There's an interesting trick in the code Daryl added to Microchess:
Code:
        AND   #$01     ; is board square white or blk?
        BNE   Pout25   ; black, print space
        LDA   #"*"     ; white, print *
        .BYTE $2c      ; used to skip over LDA #$20
Pout25  LDA   #$20     ; ASCII space
        JSR   syschout ; print one ASCII CHR - space

If the branch is not taken, the 6502 will execute
Code:
        AND   #$01     ; is board square white or blk?
        BNE   Pout25   ; black, print space
        LDA   #"*"     ; white, print *
        BIT   $20A9    ; almost a NOP
Pout25 
        JSR   syschout ; print one ASCII CHR - space

so this is a compact way to skip over the branch-taken code without a second branch.

It might be a bit of a challenge to a disassembler, or some types of emulator.


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Sun Aug 15, 2010 6:34 pm 
Offline
User avatar

Joined: Fri Aug 30, 2002 9:02 pm
Posts: 1748
Location: Sacramento, CA
That method has been around for a while. I first discovered it in the Laser 128 (Apple ][c clone) system monitor code. If you search way back - possibly back into the Delphi Forum - you might find a discussion on it. (My user ID on that board was "65c02nut")

By confusing the disassembler intentionally, it made reverse engineering a little harder - a crude form of copy guard!

Daryl


Top
 Profile  
Reply with quote  
PostPosted: Sun Aug 15, 2010 6:35 pm 
Offline
User avatar

Joined: Fri Dec 11, 2009 3:50 pm
Posts: 3367
Location: Ontario, Canada
[Edit: Besides using BIT to skip 2 bytes, variations on this trick allow you to skip either 1 byte or 2, and to use instructions other than BIT. We land on this topic in a later thread. The variations can -- unlike BIT -- preserve the state of the flags in the cpu P register. Also, the subtle but serious problem of destructive reads, and some corrective measures, are explained.]


I guess nowadays maybe there's not much justification for these insane, byte-saving tricks, but I find them fascinating just the same. It seems like there's always another byte you can shave somewhere.

Speaking of which, Daryl's sequence might better begin:
Code:
        ROR   A     ; ONE BYTE, NOT 2
        BCS   Pout25   ;
I assume it's OK to bomb Carry. (And yes, even the NMOS '02 has a ROR A instruction.)

BigEd wrote:
It might be a bit of a challenge to a disassembler, or some types of emulator.

True enough. But it's the people I feel sorry for! :wink:

-- Jeff


Last edited by Dr Jefyll on Fri Jun 03, 2016 8:15 pm, edited 1 time in total.

Top
 Profile  
Reply with quote  
PostPosted: Sun Aug 15, 2010 7:38 pm 
Offline
User avatar

Joined: Tue Mar 02, 2004 8:55 am
Posts: 996
Location: Berkshire, UK
Dr Jefyll wrote:
Cool! I guess nowadays maybe there's not much justification for these insane, byte-saving tricks, but I find them fascinating just the same. It seems like there's always another byte you can shave somewhere.

Where there's an EPROM there's always need for some space saving tricks.

When I was writing language ROMs for the BBC Microcomputer I frequently ran out of space in 16K available and spent days going over listings looking for common code sequences to eliminate or text to compress.

The ROM for my BBC database package was a tight fit in 16K before Acorn asked us if we could create a multi-user version for ECONET (an RS-422 serial based network like AppleTalk).

_________________
Andrew Jacobs
6502 & PIC Stuff - http://www.obelisk.me.uk/
Cross-Platform 6502/65C02/65816 Macro Assembler - http://www.obelisk.me.uk/dev65/
Open Source Projects - https://github.com/andrew-jacobs


Top
 Profile  
Reply with quote  
PostPosted: Sun Aug 15, 2010 7:40 pm 
Offline

Joined: Tue Jul 05, 2005 7:08 pm
Posts: 1043
Location: near Heidelberg, Germany
BigEd wrote:
so this is a compact way to skip over the branch-taken code without a second branch.


You can even use the short form of BIT, i.e. BIT ZP, to skip over a single byte opcode like SEC or the like

This trick's been around for a while indeed :-)

André


Top
 Profile  
Reply with quote  
PostPosted: Sun Aug 15, 2010 9:35 pm 
Offline
User avatar

Joined: Fri Dec 11, 2009 3:50 pm
Posts: 3367
Location: Ontario, Canada
fachat wrote:
You can even use the short form of BIT, i.e. BIT ZP, to skip over a single byte opcode
Absolutely. CMP and BIT are both handy for this trick. (They consume bytes but don't alter X Y or A.)

One suggestion, though: Use BIT Immediate, not BIT ZPG. The latter will read from an address that might map to an IO device; with some IO devices a superfluous read can erroneously update their internal status flags. This is the so-called "destructive read" phenomenon. BIT Immediate is just as effective, and avoids the danger. It also happens to be 1 cycle faster. (The original code also risks a destructive read. In that case no Immediate alternative exists -- two bytes needed to be skipped over, but 6502 Immediate operands are single byte only.) (A 65c816, however... )

I wrote:
byte-saving tricks [...] I find them fascinating

My latest version is two bytes shorter than the original!! Do I need to get a life? :roll:
Code:
   ROR A          ;pertinent bit moved to Carry
   LDA # $2F      ;A= $2F
   ADC # 0        ;A= $2F or $30
   AND # $2A      ;A= $2A or $20
   JSR   syschout ; print one ASCII CHR - space

-- Jeff


Last edited by Dr Jefyll on Sun Aug 15, 2010 10:11 pm, edited 1 time in total.

Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Sun Aug 15, 2010 9:43 pm 
Offline
User avatar

Joined: Thu Mar 11, 2004 7:42 am
Posts: 362
The BIT technique was used in Applesoft BASIC (1978), so it's practically as old as the 6502 itself.


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Sun Aug 15, 2010 9:55 pm 
Offline
User avatar

Joined: Thu Dec 11, 2008 1:28 pm
Posts: 10985
Location: England
dclxvi wrote:
The BIT technique was used in Applesoft BASIC (1978), so it's practically as old as the 6502 itself.

...and I see now that you mention it in your opcode reference pages


Top
 Profile  
Reply with quote  
PostPosted: Sun Aug 15, 2010 10:29 pm 
Offline

Joined: Tue Jul 05, 2005 7:08 pm
Posts: 1043
Location: near Heidelberg, Germany
Dr Jefyll wrote:
fachat wrote:
You can even use the short form of BIT, i.e. BIT ZP, to skip over a single byte opcode
Absolutely. CMP and BIT are both handy for this trick. (They consume bytes but don't alter X Y or A.)

One suggestion, though: Use BIT Immediate, not BIT ZPG. The latter will read from an address that might map to an IO device;


You're absolutely right. Only the NMOS 6502 only has BIT ZP and BIT ABS, but no BIT IMM. You'd have to check whether you hit an I/O address that's endangered by a destructive read, although that is something very often overlooked.

On a side note: that's why I like the 6522 over the 6526 - the 6526 has a destructive read interrupt register, while with the 6522 you have to explicitly write to acknowledge an interrupt (if memory serves me right)

André


Top
 Profile  
Reply with quote  
PostPosted: Sun Aug 15, 2010 10:41 pm 
Offline
User avatar

Joined: Fri Dec 11, 2009 3:50 pm
Posts: 3367
Location: Ontario, Canada
fachat wrote:
with the 6522 you have to explicitly write to acknowledge an interrupt (if memory serves me right)

Better check that. [Doubletake... OK:] Bits in the Interrupt Flag Register can be explicitly cleared. But they also clear in response to reads of other reg's such as ORA, ORB, the shift reg and the timers. So the 6522 is vulnerable to superfluous reads. In any case, it's best to avoid superfluous reads if you can. Problems may be rare, but they can be time bombs, and I'm guessing they can be pure hell to identify when they do occur.
fachat wrote:
the NMOS 6502 only has BIT ZP and BIT ABS, but no BIT IMM
So CMP has more utility than BIT (when using these tricks on the NMOS '02)? I didn't realize that; it was an idle remark when I mentioned CMP. Thanks for the comment.

-- Jeff


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Sun Aug 15, 2010 11:41 pm 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8543
Location: Southern California
Quote:
with the 6522 you have to explicitly write to acknowledge an interrupt (if memory serves me right)

If you look at the IFR (interrupt flag register) figure in the 6522 data sheets, you see that every one of the interrupt flags can be cleared by reading something related. For example, T1 counter roll-over interrupt flag can be cleared merely by reading T1CL (the low byte of the Timer-1 counter). This shortens the ISR code partly because it's often something you'll have to read anyway to service the interrupt, so why not have it clear the flag. It does present a problem however with reading that byte for a random-number generator if you're keeping time with T1 interrupts.


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Sun Aug 15, 2010 11:48 pm 
Offline

Joined: Sat Jan 04, 2003 10:03 pm
Posts: 1706
Just as the 6502 automatically sets flags whenever you change the accumulator, so too do I prefer destructive reads of interrupt pending flags. Manually clearing those bits are nothing but a nuisance.

The reason my Kestrel-2 emulator's chipset doesn't automatically clear interrupt pending bits is because I asked myself how things would be different if they didn't auto-clear. Also, the CPU could write into the register to artificially create an interrupt condition (why would you want to do this? I dunno -- but the Agnus' interrupt pending bits work the same way. I figured I could learn something by emulating how the Agnus worked). It turns out I never used the feature, and manually clearing the bits only made the ISR that much more inconvenient to write.


Top
 Profile  
Reply with quote  
PostPosted: Mon Aug 16, 2010 4:37 am 
Offline
User avatar

Joined: Thu May 28, 2009 9:46 pm
Posts: 8503
Location: Midwestern USA
BigEd wrote:
There's an interesting trick in the code Daryl added to Microchess:
Code:
        AND   #$01     ; is board square white or blk?
        BNE   Pout25   ; black, print space
        LDA   #"*"     ; white, print *
        .BYTE $2c      ; used to skip over LDA #$20
Pout25  LDA   #$20     ; ASCII space
        JSR   syschout ; print one ASCII CHR - space

This is an ancient programming technique that has been around as long as the 6502. A similar method for a single byte jump would be:

Code:
         clc
         .byte $24            ;BIT zp instruction
         sec
         ...program continues...


If executed from the CLC instruction, the MPU will see:

Code:
         clc
         bit $38             ;BIT zp instruction
         sec
         ...program continues...


You should consider that using the BIT opcode as illustrated in Daryl's code opens the door to a potential booby-trap. Depending on the value being loaded into the accumulator at POUT25, the BIT operation could inadvertently touch a chip register, with undefined results. For example, suppose the code is:

Code:
        AND   #$01
        BNE   Pout25
        LDA   #"*"
        .BYTE $2c
Pout25  LDA   #$D0
        JSR   syschout


What the MPU would see immediately before POUT25 would be BIT $D0A9. A lot of 6502 systems have their I/O in the $D000 block and often do not fully decode the address space. Depending on the I/O space address decoding, that BIT $D0A9 could be seen by hardware as BIT $D009. On my POC unit:

Code:
D009             sr_92b   =io_acia+dr_srb       ;ch B status (R)


which means a BIT on that address causes a read operation on the DUART's channel B status register. No telling what might happen in such a case.

_________________
x86?  We ain't got no x86.  We don't NEED no stinking x86!


Top
 Profile  
Reply with quote  
PostPosted: Mon Aug 16, 2010 5:41 am 
Offline
User avatar

Joined: Thu Dec 11, 2008 1:28 pm
Posts: 10985
Location: England
Dr Jefyll wrote:
fachat wrote:
the NMOS 6502 only has BIT ZP and BIT ABS, but no BIT IMM
So CMP has more utility than BIT (when using these tricks on the NMOS '02)
Now and again I use this chart as a nice visual reference for which CPUs have which instructions. (The chart doesn't cover the Rockwell and WDC versions of the 65C02, but there's a section near the end of the page discussing that.)

Ref: Neil Parker's "The 6502/65C02/65C816 Instruction Set Decoded" explains the instruction set encoding.


Last edited by BigEd on Mon Dec 21, 2020 1:53 pm, edited 1 time in total.

Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Mon Aug 16, 2010 10:24 pm 
Offline
User avatar

Joined: Thu Dec 11, 2008 1:28 pm
Posts: 10985
Location: England
dclxvi wrote:
The BIT technique was used in Applesoft BASIC (1978), so it's practically as old as the 6502 itself.

I did a bit more digging: looks like it's used something over a dozen times in the Microsoft BASIC of the day (see this table of 8 versions from 1977 on) - so that includes Applesoft BASIC 1.1 (commented disassembly) - but as far as I can see Woz didn't use it in his unrelated 4k 1.0 version.


Last edited by BigEd on Mon Dec 21, 2020 1:55 pm, edited 1 time in total.

Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 32 posts ]  Go to page 1, 2, 3  Next

All times are UTC


Who is online

Users browsing this forum: No registered users and 5 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: