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

All times are UTC




Post new topic Reply to topic  [ 13 posts ] 
Author Message
 Post subject: That BRK instruction...
PostPosted: Thu Sep 01, 2011 2:59 pm 
Offline

Joined: Thu Mar 03, 2011 5:56 pm
Posts: 284
I'm working on an embeddable 6502/65c02 assembler. While studying the instruction sets, I discovered that the BRK instruction is, in some ways, a 1-byte instruction, but in other ways it is a 2-byte instruction.

As far as I can see, the "obviously correct" interpretation is that it is a 2-byte instruction with an immediate operand that is ignored. Unfortunately, this seems to be at odds with most(?) references, and probably also most(?) existing assemblers.

My question: has any assemblers treated the BRK instruction as a 2-byte instruction?


Top
 Profile  
Reply with quote  
PostPosted: Thu Sep 01, 2011 3:58 pm 
Offline
User avatar

Joined: Thu May 28, 2009 9:46 pm
Posts: 8503
Location: Midwestern USA
rwiker wrote:
I'm working on an embeddable 6502/65c02 assembler. While studying the instruction sets, I discovered that the BRK instruction is, in some ways, a 1-byte instruction, but in other ways it is a 2-byte instruction.

As far as I can see, the "obviously correct" interpretation is that it is a 2-byte instruction with an immediate operand that is ignored. Unfortunately, this seems to be at odds with most(?) references, and probably also most(?) existing assemblers.

My question: has any assemblers treated the BRK instruction as a 2-byte instruction?

BRK is a two byte instruction in the sense that when the PC is pushed it is double-incremented. Most assemblers either ignore that feature or give you the option to assemble BRK with a following ("signature") byte, such as $EA (NOP)—the Kowalski 6502 simulator falls into the latter category.

Similarly with the W65C816S, the COP instruction double-increments the PC before pushing it to the stack, so its behavior is like that of BRK. However, the W65C816S data sheet lists COP as a two byte instruction, and my assembler enforces that.

When I wrote the M/L monitor for my W65C816S-powered POC unit I decided to assemble only one byte for BRK. Since then, I have been considering making it possible to assemble something like:
Code:
BRK

or
Code:
BRK <SIG>

where <SIG> would be an optional signature byte. However, the means by which I relate mnemonics and addressing modes to opcodes would make BRK <SIG> problematic, as the same opcode applies to either instruction. Hence in all likelihood, I will manufacture a fake instruction called INT, which would be entered into the assembler as:
Code:
INT <INTNUM>

which would be similar to the x86 instruction INT. The assembly of something like:
Code:
002000 INT EA

would be:
Code:
00 EA

which would, of course, disassemble as:
Code:
002000  BRK
002001  NOP

My recommendation would be to treat BRK as a one byte instruction, giving you the flexibility to add a signature byte if wanted.

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


Top
 Profile  
Reply with quote  
PostPosted: Thu Sep 01, 2011 4:50 pm 
Offline

Joined: Thu Mar 03, 2011 5:56 pm
Posts: 284
Thank you - that was a much better answer than my sketchy question deserved :-)

I have considered allowing both

BRK #whatever
and just
BRK

- since I'm using flex/bison to implement the assembler, this should be a doddle. Having an optional, configureable padding byte seems like a useful extension (and also something that has very little implementation cost).


Top
 Profile  
Reply with quote  
PostPosted: Thu Sep 01, 2011 4:53 pm 
Offline
User avatar

Joined: Fri Dec 11, 2009 3:50 pm
Posts: 3367
Location: Ontario, Canada
rwiker wrote:
As far as I can see [...] it is a 2-byte instruction with an immediate operand that is ignored.

That's right; the CPU reads and ignores the immediate operand "signature", skipping over it. The BRK handler may subsequently look back and read the operand, but that's something for software to implement -- or not.

rwiker wrote:
I have considered allowing both

BRK #whatever
and just
BRK

Adding a signature byte is certainly worthwhile. But making it optional could be dangerous. There's no problem if the BRK terminates your program, never to return -- and perhaps some assemblers neglect the signature on the assumption that BRK means termination. But if you do expect to resume execution it's imperative that the code following the BRK appear in the right place. You will encounter "unexpected results" if the next opcode appears in the spot where the signature belongs! :D

It seems to me the safest thing would be for your assembler to insert a default signature if the user fails to supply one. (Or at least issue a warning.)

-- Jeff


Last edited by Dr Jefyll on Thu Sep 01, 2011 4:57 pm, edited 1 time in total.

Top
 Profile  
Reply with quote  
PostPosted: Thu Sep 01, 2011 4:57 pm 
Offline
User avatar

Joined: Tue Mar 02, 2004 8:55 am
Posts: 996
Location: Berkshire, UK
rwiker wrote:
I'm working on an embeddable 6502/65c02 assembler. While studying the instruction sets, I discovered that the BRK instruction is, in some ways, a 1-byte instruction, but in other ways it is a 2-byte instruction.

As far as I can see, the "obviously correct" interpretation is that it is a 2-byte instruction with an immediate operand that is ignored. Unfortunately, this seems to be at odds with most(?) references, and probably also most(?) existing assemblers.

My question: has any assemblers treated the BRK instruction as a 2-byte instruction?

My assembler accepts BRK as either an implied instruction (no operands) or an immediate instruction with an 8-bit literal operand.

_________________
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  
 Post subject:
PostPosted: Fri Sep 02, 2011 10:22 pm 
Offline

Joined: Sun Nov 08, 2009 1:56 am
Posts: 411
Location: Minnesota
Quote:
My question: has any assemblers treated the BRK instruction as a 2-byte instruction?


Like many others here, the assembler I wrote supports both implied (no signature byte) and immediate (signature byte) address modes.

I also added zero page mode to make it look more like the COP instruction of the 65816 (at least, what my reading of the WDC standard says COP should look like. I'd've written it differently, but I wasn't there). Turnabout is fair play; my assembler also accepts immediate address mode for COP as an extension.

The main difference between the two then is that I allow BRK an implied mode that COP doesn't get.

I've got to say that I never thought about adding a warning to a "no operand" BRK instruction.


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Sat Sep 03, 2011 4:53 am 
Offline
User avatar

Joined: Fri Dec 11, 2009 3:50 pm
Posts: 3367
Location: Ontario, Canada
teamtempest wrote:
I've got to say that I never thought about adding a warning to a "no operand" BRK instruction.

I think the safest and friendliest thing is if the assembler requires you be explicit, something like this, maybe:
Code:
BRK       ;<-------- Is this an oversight? Generates a warning

BRK  0              ;is OK - programmer seems to be paying attention! LOL

BRK  whatever       ;is OK - (ditto)

MACRO BRK_NoSignature
 BRK  0
 Org $-1
END

BRK_NoSignature      ;Programmer explicitly nixes the signature - (ditto)

Cheers,

-- Jeff
[Edit: remove complicated & superfluous paragraph ]


Last edited by Dr Jefyll on Sat Sep 03, 2011 6:56 am, edited 1 time in total.

Top
 Profile  
Reply with quote  
PostPosted: Sat Sep 03, 2011 5:34 am 
Offline
User avatar

Joined: Thu May 28, 2009 9:46 pm
Posts: 8503
Location: Midwestern USA
Just my opinion, but I think you guys are making it too complicated. If it were me, I'd assemble BRK as an instruction with no operand and create the fake instruction INT <SIG> for cases where a signature byte is to follow the BRK opcode—this can be easily implemented as a macro.

Alternatively, since WDC describes BRK as "...a 2 byte instruction. The NMOS and CMOS devices simply skips the second byte (i.e. doesn’t care about the second byte) by incrementing the program counter twice.", make your assembler treat it as a two-byte instruction and require the signature byte. Having it both ways, I think, opens the door to programming anomalies.

My reasoning behind this is 65xx instructions with multiple addressing modes for the same mnemonic have unique opcodes, for example, ROL <ADDR> and ROL A (rotate accumulator). BRK and BRK <SIG> would have the same opcode, requiring special case testing in the assembler to determine what the programmer is trying to accomplish.

If the assembler is compliant with the WDC published language standards, implied instructions that have the same mnemonic as non-implied instructions (again, ROL <ADDR> vs. ROL A) never have a blank field for the operand, making it clear to both the assembler and the programmer how the instruction should be assembled. How would you reconcile that with a case where BRK might or might not have an operand?

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


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Sat Sep 03, 2011 6:19 am 
Offline
User avatar

Joined: Fri Dec 11, 2009 3:50 pm
Posts: 3367
Location: Ontario, Canada
I agree, actually. But my explanation was poor; I did make it sound overly complicated! :oops:
BigDumbDinosaur wrote:
make your assembler treat it as a two-byte instruction and require the signature byte. Having it both ways, I think, opens the door to programming anomalies.

Yes. Having it both ways is asking for trouble. I support treating it as a two-byte instruction and requiring the signature byte, as you say. Since it's required, that means there'll be a warning if it's absent (ie, a warning if the BRK mnemonic in the source code isn't accompanied by an operand to specify the signature byte).

-- Jeff
[ Edited to add clarification to last sentence]


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Sat Sep 03, 2011 3:42 pm 
Offline

Joined: Sun Nov 08, 2009 1:56 am
Posts: 411
Location: Minnesota
Quote:
If the assembler is compliant with the WDC published language standards, implied instructions that have the same mnemonic as non-implied instructions (again, ROL <ADDR> vs. ROL A) never have a blank field for the operand, making it clear to both the assembler and the programmer how the instruction should be assembled. How would you reconcile that with a case where BRK might or might not have an operand?


I'm not at all sure I understand your point. It seems to me it's pretty clear to both: either there's no operand (therefore one $00 byte) or there is (therefore $00+$value).

The fact that the BRK opcode itself is always $00 is of no importance. One of the reasons for an assembler in the first place is that the numeric values of the opcodes are hidden precisely because the programmer shouldn't have to remember what they are.

Consider the case of WDC-recommended aliases such as BGE for BCS. Does the fact that these resolve to the same opcode matter in any practical sense?

It is true that BRK's incrementing the PC by two before pushing it does mean that the programmer must account for that when trying to purposefully use BRK. It is not true that using a signature byte is the only way to do so. A programmer could, for example, decrement the saved PC by one as part of the service routine.

So I would be reluctant to require a signature byte. What I probably will do, though, is update the documentation to make special note of BRK and to offer an example INT <sig> macro to those who want to make sure there always is one. Something like this, perhaps:

Code:
.macro INT, ]sig$=""
.if ]sig$
BRK val(]sig$)
.else
.error "Signature byte required"
.endif
.endm


An alternative would be to allow a macro named BRK to override the instruction named BRK, but in my design it's very easy to add address modes to instructions and rather harder to override instruction names with macros. There are several reasons for allowing it, though, so I'm thinking about ways to do so.


Top
 Profile  
Reply with quote  
PostPosted: Sat Sep 03, 2011 4:29 pm 
Offline
User avatar

Joined: Thu May 28, 2009 9:46 pm
Posts: 8503
Location: Midwestern USA
teamtempest wrote:
Quote:
If the assembler is compliant with the WDC published language standards, implied instructions that have the same mnemonic as non-implied instructions (again, ROL <ADDR> vs. ROL A) never have a blank field for the operand, making it clear to both the assembler and the programmer how the instruction should be assembled. How would you reconcile that with a case where BRK might or might not have an operand?

I'm not at all sure I understand your point.

My point is that BRK, like CLC and INX, is a unique instruction, in that the mnemonic can only resolve to a single opcode ($00). All 65xx mnemonics that can resolve to more than one opcode require an operand of some sort to disambiguate the instruction. To maintain uniformity, BRK should be treated no differently. BRK takes no operand. If the programmer wishes to include a signature byte, either the assembler should include an alias for BRK that demands the entry of an operand, or a macro should be used for disambiguation.

Quote:
It seems to me it's pretty clear to both: either there's no operand (therefore one $00 byte) or there is (therefore $00+$value).

Clear to you and me, yes. However, an assembler can't determine intent. It can only react to what it encounters in the source code.

Quote:
Consider the case of WDC-recommended aliases such as BGE for BCS. Does the fact that these resolve to the same opcode matter in any practical sense?

Apples and oranges. BGE as an alias for BCS simply means two different mnemonics resolve to the same opcode ($B0) that always takes the same type of operand (a branch offset). My earlier example of using INT to assemble a BRK instruction with a signature is also a form of aliasing. In both cases, the fundamental nature of the underlying machine operation is the same. The only thing different is the way in which the source instruction is phrased.

Quote:
It is true that BRK's incrementing the PC by two before pushing it does mean that the programmer must account for that when trying to purposefully use BRK. It is not true that using a signature byte is the only way to do so. A programmer could, for example, decrement the saved PC by one as part of the service routine.

You are correct and thus have highlighted the fact that BRK itself takes no operand. The double-incrementing of the PC had to do with BRK's original purpose, which was the facilitation of PROM patching. However, BRK can also be used to transfer program control to a supervisor function, with the signature byte indicating to the supervisor the desired operation—a procedure analogous to the INT 0x21 sequence used to call services in MS-DOS. The latter application would be a case where programmer intent would demand the use of the signature byte.

As an aside, the W65C816S makes using the INT <INTNUM> method of calling system services trivial, thanks to the stack relative instructions.

Quote:
So I would be reluctant to require a signature byte.

So would I, as mentioned a few posts ago. Continue to treat the BRK mnemonic as an instruction that takes no operand.

Quote:
What I probably will do, though, is update the documentation to make special note of BRK and to offer an example INT <sig> macro to those who want to make sure there always is one. Something like this, perhaps:

Code:
.macro INT, ?sig=""
.if "?sig"
BRK ?sig
.else
.error "Signature byte required"
.endif
.endm

Gee, where did that idea come from? :lol: You'd want to include a test to verify that the ?sig parameter is in the range $00-$FF.

Quote:
An alternative would be to allow a macro named BRK to override the instruction named BRK...

That I wouldn't do. Aside from the possible confusion it would cause, it wouldn't work with an assembler that prohibits replacing a standard 65xx mnemonic with a macro (the Kowalski simulator's assembler is in that category).


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Sat Sep 10, 2011 1:35 am 
Offline

Joined: Fri Oct 17, 2008 5:48 pm
Posts: 4
I lean towards treating BRK as an implied instruction that triggers an interrupt that was meant to be used for debugging and, therefore, most likely not intended to be returned from.


Top
 Profile  
Reply with quote  
 Post subject: Is BRK broken?
PostPosted: Sat Sep 10, 2011 2:55 am 
Offline
User avatar

Joined: Thu May 28, 2009 9:46 pm
Posts: 8503
Location: Midwestern USA
doppelheathen wrote:
I lean towards treating BRK as an implied instruction that triggers an interrupt that was meant to be used for debugging and, therefore, most likely not intended to be returned from.

Actually, BRK is a stack instruction according to the WDC literature. Also, although BRK is very often used for debugging, especially inside a machine language monitor, that was not its original purpose.

Prior to the ready availability of EPROMs (and, later, EEPROMs), BRK was used to assist in patching PROMs. Since the BRK opcode is $00, "blowing" all the fuses at a particular address and selectively blowing the fuses at the next address would set up BRK followed by a signature byte. In the PROM, the IRQ/BRK vector pointed to some front end code that, if the interrupt was a BRK, would route the MPU to a patch area in the PROM. To allow more than one patch to be in the ROM, the signature byte would be gotten by sniffing the stack to get the value of the PC that was pushed when BRK was executed. The signature byte would tell the patch handler which patch to execute. Code could be burned into that patch area that ultimately returned control either to the address two bytes beyond the BRK instruction (with an RTI), or elsewhere in the ROM by tinkering with the PC value on the stack.

So BRK was not an instruction "...not intended to be returned from." An instruction from which no return is possible is STP, found in the WDC 65C... family.

Aside from the above, BRK can be used to call operating system services from a foreground program without that program having to know entry addresses. The signature byte following BRK can be used as an index into operating system services (up to 255 of them, if desired—one signature would be reserved for an actual BRK). This usage of BRK is particularly handy with the W65C816S, due to its convenient stack addressing instructions.

Code:
;W65C816S BRK handler
;
         rep #%00110000        ;select 16 bit registers
         phb                   ;save current data bank
         pha
         phx
         phy
         cli                   ;re-enable interrupts
;
;   At this point, we can retrieve the PC as pushed by
;   the BRK instruction.  Pushes were in the following
;   sequence when BRK was executed & the BRK handler
;   called:
;
;       PBR  program bank, 1 byte
;       PCH  program counter MSB
;       PCL  program counter LSB
;       SR   status register, 1 byte
;       DB   data bank, 1 byte
;       .A   accumulator, 2 bytes
;       .X   X-register, 2 bytes
;       .Y   Y-register, 2 bytes
;
;   If we treat the current stack pointer as zero, the
;   PC is 9 bytes up the stack.  Hence we can retrieve
;   PC as follows:
;
         lda 9,s               ;get PC
;
;   The signature byte is 1 byte before the PC location,
;   so the following code will get the signature byte:
;
         dec a                 ;move back 1 address
         sep #%00100000        ;switch .A to 8 bits
         lda #$00
         xba                   ;zero the B-accumulator
         lda $00,x             ;get the signature byte
;
;   The LDA $00,X works because the index registers are 16
;   bits wide, making it possible to index the full 64K
;   program space with the X-register.
;
;   If the signature byte is an index into a supervisor
;   program's services, the rest is basic machine code:
;
         rep #%00100000        ;switch accumulator back to 16 bits
         asl a                 ;double signature byte for offset, which is now a...
         tax                   ;16 bit index into a...
         jmp (functab,x)       ;dispatch table
;
;   Alternatively, you could call your function with JSR (FUNCTAB,X) &
;   have the MPU return to a common exit point immediately after the
;   dispatch instruction.  If you do so, you have to adjust all your stack
;   references by 2 to account for the return address pushed by the JSR.

That zero byte is more useful than one might think at first blush! Although not directly applicable, reading up on the x86 INT instruction would give you some idea as to what can be done with a software interrupt.

[Edit: fixed an omission in the code. —BDD]

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


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

All times are UTC


Who is online

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