That BRK instruction...
That BRK instruction...
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?
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?
- BigDumbDinosaur
- Posts: 9428
- Joined: 28 May 2009
- Location: Midwestern USA (JB Pritzker’s dystopia)
- Contact:
Re: That BRK instruction...
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?
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?
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: Select all
BRKCode: Select all
BRK <SIG>Code: Select all
INT <INTNUM>Code: Select all
002000 INT EACode: Select all
00 EACode: Select all
002000 BRK
002001 NOPx86? We ain't got no x86. We don't NEED no stinking x86!
Re: That BRK instruction...
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).
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).
Re: That BRK instruction...
rwiker wrote:
As far as I can see [...] it is a 2-byte instruction with an immediate operand that is ignored.
rwiker wrote:
I have considered allowing both
BRK #whatever
and just
BRK
BRK #whatever
and just
BRK
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.
- BitWise
- In Memoriam
- Posts: 996
- Joined: 02 Mar 2004
- Location: Berkshire, UK
- Contact:
Re: That BRK instruction...
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?
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?
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
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
-
teamtempest
- Posts: 443
- Joined: 08 Nov 2009
- Location: Minnesota
- Contact:
Quote:
My question: has any assemblers treated the BRK instruction as a 2-byte instruction?
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.
teamtempest wrote:
I've got to say that I never thought about adding a warning to a "no operand" BRK instruction.
Code: Select all
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)-- Jeff
[Edit: remove complicated & superfluous paragraph ]
Last edited by Dr Jefyll on Sat Sep 03, 2011 6:56 am, edited 1 time in total.
- BigDumbDinosaur
- Posts: 9428
- Joined: 28 May 2009
- Location: Midwestern USA (JB Pritzker’s dystopia)
- Contact:
Re: That BRK instruction...
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?
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!
I agree, actually. But my explanation was poor; I did make it sound overly complicated!
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]
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.
-- Jeff
[ Edited to add clarification to last sentence]
-
teamtempest
- Posts: 443
- Joined: 08 Nov 2009
- Location: Minnesota
- Contact:
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?
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: Select all
.macro INT, ]sig$=""
.if ]sig$
BRK val(]sig$)
.else
.error "Signature byte required"
.endif
.endm
- BigDumbDinosaur
- Posts: 9428
- Joined: 28 May 2009
- Location: Midwestern USA (JB Pritzker’s dystopia)
- Contact:
We interrupt this program...
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?
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).
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?
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.
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.
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: Select all
.macro INT, ?sig=""
.if "?sig"
BRK ?sig
.else
.error "Signature byte required"
.endif
.endm
Quote:
An alternative would be to allow a macro named BRK to override the instruction named BRK...
-
doppelheathen
- Posts: 4
- Joined: 17 Oct 2008
- BigDumbDinosaur
- Posts: 9428
- Joined: 28 May 2009
- Location: Midwestern USA (JB Pritzker’s dystopia)
- Contact:
Is BRK broken?
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.
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: Select all
;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.[Edit: fixed an omission in the code. —BDD]
x86? We ain't got no x86. We don't NEED no stinking x86!