Jump to content
shazz

The mysterious BIT instruction....

Recommended Posts

Hi again,

 

Can somebody explain me the BIT instruction...

 

In some pieces of code I read :

- NOP, BIT $ea

- BIT GRP0

 

I read that you can do :

.BYTE $2C

LDX #$20 => $2C $A2 $20 so it is equals to a BIT $20A2... yes I agree... an so ???? As A is not defined, what is the goal to do ??? AND ($20A2) ?

 

It looks like BIT can be used to do something else than A AND (MEM) => Z/V/N but I don't get it....

 

I feel stupid...

Share this post


Link to post
Share on other sites

I read the same thing you did, and my impression was that it had to do with JUST setting the flags without actually doing anything else. Could be wrong there, I'm no 6502 expert.

Share this post


Link to post
Share on other sites

I don't know why somebody would use BIT GRP0 - it's probably related to mirrored memory as reading GRP0 is the same as reading INPT3 (the 4th paddle).

 

 

What you'll normally see BIT used for in Atari programs is for testing the collision registers, firebuttons and paddle ports (see page 46). When you BIT something, the value in bit 7 is moved into the N flag (they call it S in that documentation - it's the Negative flag, or Sign) and the value in bit 6 is moved into the V flag.

 

So this code would check for collisions between the players and the missiles:

  bit CXM0P ; bit 7 = Missile 0 & Player 1, bit 6 = Missile 0 & Player 0
 bpl .skip1
 jsr ProcessMissile0Player1Collision
.skip1
 bvc .skip2
 jsr ProcessMissile0Player0Collision
.skip2
 bit CXM1P ; bit 7 = Missile 1 & Player 0, bit 6 = Missile 1 and Player 1
 bpl .skip3
 jsr ProcessMissile1Player0Collision
.skip3
 bvc .skip4
 jsr ProcessMissile1Player1Collision
.skip4
 ...

 

ProcessMissile0Player1Collision and ProcessMissile1Player0Collision need to preserve the state of V. They can do so by using PHP at the start, then PLP at the end, or by not doing anything that changes V. Another solution would be to just do another BIT test after .skip1 and .skip3.

 

This code would check if the players hit FIRE

  bit INPT4  ; bit 7 on = fire not pressed, bit 7 off = fire pressed
 bmi .skip5   
 jsr ProcessLeftFireButton
.skip5
 bit INPT5  ; bit 7 on = fire not pressed, bit 7 off = fire pressed
 bmi .skip6
 jsr ProcessRightFireButton
.skip6
 ...

Share this post


Link to post
Share on other sites

Regarding the AND function the difference is that it drops the result in the bit bucket behind the CPU rather than updating the accumulator with the ANDed value.

It might make it easier to follow that the V and N flags happen to be bits 6 and 7 of the condition code register; consider these bits aren't being used to represent overflow or negative conditions in this instance though, they are just copied over from whatever you BIT into :)

Share this post


Link to post
Share on other sites

The $2c trick is used to skip the next 2 bytes by turning them into a harmless bit instruction. Its usually used with a proceeding branch to the LDX. The magic is, following the branch and not following the branch will take the same number of cycles.

Share this post


Link to post
Share on other sites

I don't know why somebody would use BIT GRP0 - it's probably related to mirrored memory as reading GRP0 is the same as reading INPT3 (the 4th paddle).

Since BIT is a type of read instruction (loads from memory rather than stores to memory), and GRP0 is a write-only address, I'd say any code that contains "BIT GRP0" was either "poorly" written using the "wrong" label for the intended address, or it was disassembled "wrong" using a write-only address where it should have used a read-only address.

 

I remember another thread-- I think it was actually in the stella mailing list-- in which someone asked about a section of code where a game was reading (loading) the TIM1T register (IIRC), which would actually have the same effect as reading INTIM, since TIM1T is $294 and INTIM is $284, but because of the way mirroring works within the RIOT addresses, $294 is a mirror of $284 (IIRC) when performing a read instruction. So in that case either the "LDA TIM1T" code was a lucky programming mistake that happened to work, or the programmer had defined INTIM as $294 and the disassembler was interpreting it as TIM1T. Note, it may have been TIM8T ($295) and TIMINT ($285), but the explanation is still the same-- mirroring within the RIOT, coupled with the state of the R/W line.

 

Sorry for the digression from the BIT instruction, but unless someone's looking at the original source code, my guess is that it's most likely a disassembly boo-boo. :) Mirroring, coupled with read-only and write-only addresses, can complicate disassembly a bit.

Share this post


Link to post
Share on other sites

BIT is often used to save a byte in a case where there is an always branch to an address 1 or 2 bytes ahead. Inside of a kernel BIT is often used in time critical areas to help even out cycles so not matter what path the program follows, the cycles are the same.

 

 

Illegal NOP's are used by a lot of 2600 programmers instead of BIT, since all flags are preserved.

 

skip 1 byte with:

BIT zeropage = $24

NOP zeropage = $04 ; <--- this is used by the SLEEP marco in macros.h

 

skip 2 bytes with:

BIT absolute = $2C

NOP absolute = $0C

 

 

Illegal nops and BIT's are also good for bankswitching purposes.

Share this post


Link to post
Share on other sites

I remember another thread-- I think it was actually in the stella mailing list-- in which someone asked about a section of code where a game was reading (loading) the TIM1T register (IIRC), which would actually have the same effect as reading INTIM, since TIM1T is $294 and INTIM is $284, but because of the way mirroring works within the RIOT addresses, $294 is a mirror of $284 (IIRC) when performing a read instruction. So in that case either the "LDA TIM1T" code was a lucky programming mistake that happened to work, or the programmer had defined INTIM as $294 and the disassembler was interpreting it as TIM1T. Note, it may have been TIM8T ($295) and TIMINT ($285), but the explanation is still the same-- mirroring within the RIOT, coupled with the state of the R/W line.

 

If you are going to go a BIT off-topic, then I will too. :)

 

 

The $2xx registers (TIM64T, INTIM, etc...) can be jumped between operator and operand in some cases for new instructions:

 

Operands:

SWCHA ===> nop #$02 ; waste 2 cycles

SWCHB ===> nop #$02 ; waste 2 cycles

SWBCNT ===> sax (WSYNC,X)

INTIM ===> sty WSYNC

TIMINT ===> sta WSYNC

TIM1T ===> sty WSYNC,X

TIM8T ===> sta WSYNC,X

TIM64T ===> stx WSYNC,Y

T1024T ===> sax WSYNC,Y

 

 

Very limited use, but interesting. One more interesting thing is using INTIM as an address. How it works is you INTIM will always have a value $00-$FF, while the register read for the high address (TIMINT) will always point to zeropage or one of its mirrors. So conceivably you can set the timer to decrement with one of the timers, and jump into a zero page location.

 

 

It would be fun to run some self modifying code in zero page that kept using INTIM to loop itself both as an address and counter. This is something I wanted to try at one point, just for this:

 

 

jmp.ind (INTIM)

 

 

There is just something damn cool about jumping in time. :grin: Kinda like Quantum Leap.

  • Like 1

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...

  • Recently Browsing   0 members

    No registered users viewing this page.

×
×
  • Create New...