Are there any coders still around being able to help me a bit?
TIA
Ben
Ben Castricum skrev i meddelandet <6q3jru$74b$1...@titan.rotterdam.nl>...
>Since it is possible to load original tapes into ccs64, I am trying to
>"hack" a game so I can make a nice clean disk version of it. I understand
>the 6502 opcodes so I managed to use the loader-routine and save all
>relevant data in a file. However if I restore the file into memory the NMI
>and the IRQ contain values I did not expect them to have. I used to think
>that $0314 holds the IRQ value, but this doesn't seem to be the case.
That's correct (normally $0314:31 EA => $EA31, the standard Kernal IRQ).
>Are there any coders still around being able to help me a bit?
Are you sure that the dump file you created from the loader is being
loaded back into the correct place in memory? If you post the relevant
section of assembly, someone may be able to better guess what it's up to.
How large is the dump?
--
-------------- The Commodore 64 lives: http://computerworkshops.home.ml.org/ --
Cameron Kaiser (posting with a Commodore 128) | "When in doubt, take a pawn."
cdkaiser@concentricMUNGEnet | -- Mission: Impossible
-- personal page: http://calvin.ptloma.edu/~spectre/ ------ CBMSF Unit $EA31 --
>True $0314/15 holds the IRQ and false. Use $fffe/$ffff as these points to
>$0134/$0315 ahh i cant explain exactly
You're right. The vector on $fffe points to $ff48. Here's the disassembly:
ff48: 48 pha
ff49: 8a txa
ff4a: 48 pha
ff4b: 98 tya
ff4c: 48 pha
ff4d: ba tsx
ff4e: bd 04 01 lda 0104,x
ff51: 29 10 and #10
ff53: f0 03 beq ff58
ff55: 6c 16 03 jmp (0316)
ff58: 6c 14 03 jmp (0314)
When an IRQ comes along, the 65xx CPU jumps through the vector Dahl indicated.
What this does is stuff the registers on the stack and pick the vector it
will jump through. The 60-times-a-second-IRQ is vectored at $0314, which is
why everyone uses that.
Which, of course, fetches an unreliable value if --- for some reason
(i.e., it's in the $FB+ range) --- the stack pointer throws you over the
page boundary into $0200+. Yet another sad, sad Commodore ROM bug. :( If
you are really using BRK as a practical (and expansive) opcode, you've just
GOTTA keep track of that stack pointer... Or just burn a new ROM with
better BRK handling code in it.
Try out the following code, Cameron:
SEI
LDX #$00
TXA
.LOOP STA $01FF,X
INX
BNE .LOOP
DEX
TXS
BRK
BRK
BRK
BRK
-Now check your BRK driver to see where the program flow went. ;)
Gets even trickier on a C-128 that has multiple BRK handlers and you have
been flipping your zero page and stack around, seeing as how it seems so
mighty clever having a half dozen zero pages, and just as many stacks, and
a comparable number of BRK handlers. Life can get awful complicated if
you have enough spare time on your hands...
--
And while we are at the C64 rom code; I always wondered why the rom code
copies a bunch of vectors normally found at the $0300 location to $FD30
during a reset! Anyone knows what's the idea behind that?
Cameron Kaiser wrote in message <6q52qk$g...@examiner.concentric.net>...
>"Dahl" <no-spam-to:da...@c64.org> writes:
>
>>True $0314/15 holds the IRQ and false. Use $fffe/$ffff as these points to
>>$0134/$0315 ahh i cant explain exactly
>
>You're right. The vector on $fffe points to $ff48. Here's the disassembly:
>
>ff48: 48 pha
>ff49: 8a txa
>ff4a: 48 pha
>ff4b: 98 tya
>ff4c: 48 pha
>ff4d: ba tsx
>ff4e: bd 04 01 lda 0104,x
>Thanks, I think this will a great help in my quest :) Does the NMI has such
>a pointer as well?
Yes, at $0318. The actual 65xx vector is at $fffa/b; it points to this routine:
fe43: 78 sei
fe44: 6c 18 03 jmp (0318)
$0318 is vectored to $fe47 by default, which does stuff like poll for
cartridges in the $8000-$[9b]fff addressing space and call them if present
(allowing you to start cartridge dump files by simply loading them into the
$8000 address space and tapping RESTORE), and then determine if RUN-STOP is
pressed (if so, go to warm start; otherwise, ignore it), etc.
Other useful 65xx vectors are $fffc/d, which is the general cold start (a
JMP ($FFFC) on *any* 6500-series CPU will reset the computer), and of course
$fffe/f, the IRQ vector.
In article <6q6bif$kc6$1...@titan.rotterdam.nl>,
Ben Castricum <B.Cas...@bns.getronics.nl> wrote:
>Thanks, I think this will a great help in my quest :) Does the NMI has such
>a pointer as well?
The 6510 has three hardware vectors:
$FFFA NMI vector (normally points to $FE43)
$FFFC Reset vector (points to $FCE2 = 64738)
$FFFE Interrupt handler (normally $FF48)
Now for the prize question: do you know how to mask an NMI? :)
>And while we are at the C64 rom code; I always wondered why the rom code
>copies a bunch of vectors normally found at the $0300 location to $FD30
>during a reset! Anyone knows what's the idea behind that?
It's the other way around -- the system vectors are copied from the ROMs
at $FD30 into the $0300 area in RAM.
>Cameron Kaiser wrote in message <6q52qk$g...@examiner.concentric.net>...
>>
>>You're right. The vector on $fffe points to $ff48. Here's the disassembly:
>>
>>ff48: 48 pha
>>ff49: 8a txa
>>ff4a: 48 pha
>>ff4b: 98 tya
>>ff4c: 48 pha
>>ff4d: ba tsx
>>ff4e: bd 04 01 lda 0104,x
>>ff51: 29 10 and #10
>>ff53: f0 03 beq ff58
>>ff55: 6c 16 03 jmp (0316)
>>ff58: 6c 14 03 jmp (0314)
>>
>>When an IRQ comes along, the 65xx CPU jumps through the vector Dahl
>>indicated.
More precisely, when an interrupt -- software or hardware -- comes along.
This includes BRK -- a software interrupt -- which is why the code is
there to check the status register for the break flag (AND #$10).
Compare with the 65816, which has separate hardware vectors for BRK and
for IRQs.
-S
I think not.
>Try out the following code, Cameron:
>
> SEI
> LDX #$00
> TXA
> .LOOP STA $01FF,X
> INX
> BNE .LOOP
> DEX
> TXS
> BRK
> BRK
> BRK
> BRK
>
>-Now check your BRK driver to see where the program flow went. ;)
Well, I tried it, and it worked fine. In fact, I tried it with three
different monitors, and it worked fine. Filling page 2 with zeros
caused one of them to crash, but the monitor correctly calculated PC
SP, and the processor regs -- and the C= ROM routine worked just fine.
As well it ought to. You neglected the preceding part of the ROM code:
>>ff48: 48 pha
>>ff49: 8a txa
>>ff4a: 48 pha
>>ff4b: 98 tya
>>ff4c: 48 pha
>>ff4d: ba tsx
>>ff4e: bd 04 01 lda 0104,x
>>ff51: 29 10 and #10
>>ff53: f0 03 beq ff58
>>ff55: 6c 16 03 jmp (0316)
>>ff58: 6c 14 03 jmp (0314)
BRK pushes 3 bytes onto the stack, and three more pha in the ROMs makes
it six. That's the point of the $0104, you know -- to move past the
three registers.
Out of curiosity, what did you think would happen?
-S
Oh, trivia! Wasn't it something like making the CIA generate an NMI, but
not reading the ICR/ISR and therefore leaving the NMI line on active
level? This way any other NMIs from the keyboard or external sources
won't be recognized.
Now, what's the prize?
Nicolas
>BRK pushes 3 bytes onto the stack, and three more pha in the ROMs makes
>it six. That's the point of the $0104, you know -- to move past the
>three registers.
Confessing to a lack of knowledge on this, wouldn't ldx 0104,x cross into
the next page if x were greater than 252? I think that's what Matt was
alluding to (a really full stack would cause erratic behaviour). Of course,
if SP were at 252, then it would have some real trouble stuffing even the
processor state on the stack.
But I may be very wrong :-)
>> Now for the prize question: do you know how to mask an NMI? :)
>Oh, trivia! Wasn't it something like making the CIA generate an NMI, but
>not reading the ICR/ISR and therefore leaving the NMI line on active
>level? This way any other NMIs from the keyboard or external sources
>won't be recognized.
I knew a guy who just disconnected the hardware line and put a switch there
instead. That's known as a "creative solution" :-)
">BRK pushes 3 bytes onto the stack, and three more pha in the ROMs makes
>it six. That's the point of the $0104, you know -- to move past the
>three registers.
"Confessing to a lack of knowledge on this, wouldn't ldx 0104,x cross into
the next page if x were greater than 252? I think that's what Matt was
alluding to (a really full stack would cause erratic behaviour). Of course,
if SP were at 252, then it would have some real trouble stuffing even the
processor state on the stack.".
There IS a "forbidden range" for the Stack Pointer, but it's not 252, 253,
254, or 255. It's 2 to 6. If the SP were at 255 when the 6510 Program Counter
hit a zero (BRK), PC HI would be pushed onto 511, PC LO onto 510, and the
Processor Status register onto 509. The routine at $FF48 would push A, X, and
Y onto 508, 507, and 506; SP would then be 249. The LDA $0104,X would fetch
the byte at 509, the P register; no problem. Even with SP at 1 when BRK is
encountered, P would be at 511; the register would still be fetched by the
routine. The stack wraps around; it is always on page 1. The 6 registers
would be at 257, 256, 511, 510, 509, and 508. But with SP at 2, P would be at
256, and LDA $0104,X would fetch the byte at 512. The problem exists at an SP
of 3, 4, or 5, also, but not above.
A cold start sets SP to 251; a warm start sets SP to 250. If SP were at
253 or higher (254, 255, 0, 1, ...), another problem would arise; registers
would be pushed onto address 509. That's a magic number; there should never be
a zero there. When you enter a BASIC line, it starts at 512. When BASIC
copies the line to program memory, it first writes the line number to 510, 511.
But it copies from 508 on; 508 and 509 represent the line pointers, which are
then calculated and inserted by the routine at $A533. But that routine stops
when it hits 0, X, 0. So a zero at 509 will mess up all lines after it.
--
When backing up your hard drive, shift into reverse gear S M O O T H L Y.
John
always fetches bytes from the stack!??
--
One problem being that the stack includes addresses $0100-0103 --- four
bytes that MAY have significant information not available through lda
$0104,x. Of course, a BASIC programmer probably never gets around to
using the BRK instruction in a truly creative way, either.
--
> There IS a "forbidden range" for the Stack Pointer, but it's not 252, 253,
>254, or 255. It's 2 to 6. If the SP were at 255 when the 6510 Program Counter
Oy, now I'm confused. I use the stack very little except for the occasional
PHA and of course JSR. If SP = 0, what locations would the BRK modify? In
what order would it put things on the stack? When you push things onto the
stack, what happens to SP: does it go down or up? What absolute locations in
the stack are being modified?
I'd always assumed that if SP = 0, then your next PHA will stick the
accumulator at $0100, and increment SP. Why, then, does a cold start init
SP with such a high value?
Why, a free ASCII character. And here you go:
N
Congratulations! Don't spend it all in one place.
-S
Gee, thanks! Now this "N" belongs to me and noone else may use it
anymore (at least in this group). I will take legal action against
anyone using this letter (I guess only the capital one) without paying a
license fee. One piece of Commodore equipment will be enough per person
;-)
Nicolas
The question I asked was: what do you think will happen? For example, with
the code you gave. I would like to see an example of this "sad, sad C= bug"
in action.
I do not feel that this is a complicated question, and I am still interested
in an answer.
-S
It would indeed. But it won't happen, and wouldn't matter even if it did.
That LDA $0104,X doesn't load some random byte -- it loads a very specific
byte.
Pretend you are a 6510 (you got to be VIC last time, so now it's someone
else's turn):
Start with any value you want, and subtract 3 for the BRK -- PC and P
are pushed onto the stack, so the status register is now at SP+1. Then
subtract 3 more for the three PHA's. And there's your SP+4 -- three regs,
and the status register.
That AND #$10 just checks the BRK flag in the status byte.
Obviously, there would be a problem if the stack were to overflow... but
at that point you're totally hosed anyways.
-S
The stack starts at $FF, and builds downwards.
-S
I KNOW! I used to solve it this way:
Simply point the vector to this routine (lets say $cf00)
$cf00 rti
But then... it was masked and executed... I never solved it i guess ;-)
Although only four bytes are "inaccessible" from lda $0104,x, any of these
may be one (or more) of the six bytes pushed onto the stack. It obviously
won't do if you detect the BRK flag set, but any of the other 6 bytes are
unreliable. It's a package deal. If even one is bad, it goes without
saying that a truly ambitious BRK handler (for instance, one that
provides additional instructions beyond the ordinary 6502 instruction
set) will crash at seemingly mysterious and unknown times...
--
"If SP = 0, what locations would the BRK modify? In what order would it put
things on the stack? When you push things onto the stack, what happens to SP:
does it go down or up? What absolute locations in the stack are being modified?
"I'd always assumed that if SP = 0, then your next PHA will stick the
accumulator at $0100, and increment SP. Why, then, does a cold start init
SP with such a high value?".
A push places the byte on the stack at the current SP value in page 1, and
DECREMENTS SP. A pull INCREMENTS SP, and fetches the byte at that address. If
SP = 0 and an IRQ interrupt occurs, either hardware or software (BRK opcode),
PC HI goes to 256, PC LO to 511, P to 510, and the MPU then jumps to the IRQ
vector at $FFFE (normally $FF48) with SP = 253 and the I flag set. There, A,
X, and Y, go to 509, 508, and 507. With SP = 250, LDA $0104,X will fetch the P
register at 510.
Pulling PC HI and PC LO off the stack can tell you where you are in an ML
routine. The following is from "DOX.BIN", which prints appended PETASCII text
to the screen (or a printer) no matter where the routine resides. In this
case, the routine was appended to a one-line BASIC program:
10 sys2061
The offset of start-of-text from start-of-routine is 340, but the following is
for one of the carried text strings at offset = 190.
$080D LDA #$60 ; Puts address of
$080F STA $8A ; offset = 6 onto
$0811 JSR $8A ; stack
$0814 SEC ;--------------------
$0815 LDY #$00 ; Saves byte that
$0817 STY $90 ; follows text and
$0819 LDA ($2D),Y ; replaces it with 0
$081B STA $02 ;
$081D TYA ;
$081E STA ($2D),Y ;--------------------
$0820 TSX ; Decrements SP twice
$0821 TXA ;
$0822 SBC #$02 ;
$0824 TAX ;
$0825 TXS ;--------------------
$0826 PLA ; Fetches PC LO, adds
$0827 ADC #$B7 ; 184 (carry is set),
$0829 STA $FD ; and stores into
$082B TAX ; $FD, $FE $082C PLA ;
$082D ADC #$00 ;
$082F STA $FE ;
$0831 TAY ;
On Tue, 4 Aug 1998, Nicolas Welte wrote:
> Stephen Judd wrote:
> >
> > Now for the prize question: do you know how to mask an NMI? :)
> >
>
> Oh, trivia! Wasn't it something like making the CIA generate an NMI, but
> not reading the ICR/ISR and therefore leaving the NMI line on active
> level? This way any other NMIs from the keyboard or external sources
> won't be recognized.
>
And next, how to cure this strange otherworld quantum state?
Seems at some randomish times, my computer's RESTORE key no longer
functions. I haven't given this issue too much thought though.
How do I get normality back?
> Now, what's the prize?
Hmm... How about a '2' key. :) LOL
"Obviously, there would be a problem if the stack were to overflow... but
at that point you're totally hosed anyways.".
If by "overflow", you mean pushing more than 256 bytes onto the stack, I
disagree.
$C000 LDA #$EF ; All bits set except
$C002 STA $0203 ; bit 4
$C005 LDX #$FB ;--------------------
$C007 TXS ; SP set to 251, and
$C008 JSR $C00B ; 502 bytes pushed,
$C00B DEX ; setting SP = 5
$C00C BNE $C008 ;--------------------
$C00E LDA #$2A ; Displays "*"
$C010 JSR $FFD2 ;--------------------
$C013 BRK ; MPU jumps to $FF48
$C014 BRK ;--------------------
$C015 INC $D020 ; Flashes border in
$C018 LDX #$64 ; infinite loop
$C01A INX ;
$C01B BNE $C01A ;
$C01D BEQ $C015 ;
With SP in the forbidden range (2 to 5), the routine at $FF48 fetches the
byte at $0203 (thinking that it's the P register). Since bit 4 is clear, the
indirect jump is to the IRQ vector at $0314. The system interrupt routine ends
with the six pushed bytes being pulled off the stack; the routine is otherwise
rather innocuous. The 6510 then jumps to $C015; the BRK opcode increments PC
by 2, not 1. So you see the asterisk and the flashing border. There is no SEI
opcode; the routine at $E716 (via $FFD2) ends with a CLI anyway. So the
routine does its thing, with interrupts every jiffy. All with 502 bytes
pushed onto the stack.
If the byte at $C001 is changed to one with bit 4 set, the break vector (at
$0316) will be called at the first BRK. And you'll never see all those pretty
colors. (I don't anyway; I use a black-and-white monitor.)
And my apologies to Nicolas Welte for the capital en's; couldn't the prize
have been a capital 3?
As John Iannetta pointed out (more or less), the fatal value for the stack
pointer is BEFORE the software interrupt occurs, and not after it. I was
sleepy when I posted the sample code. Naturally, the stack pointer goes
downward with every PHA. Thus, the part dex txs brk brk brk brk * * * was
in error, and you were right that your monitor program did not crash.
>I do not feel that this is a complicated question, and I am still interested
>in an answer.
I doubt you are interested in any kind of answer, but how about replacing
dex txs with ldx #4 or ldx #5 txs? The point being that the interrupt
will be interpreted as generated by an IRQ rather than a BRK. The BRK flag
wouldn't be set, and the elaborately written (or so I supposed) BRK
handler would never be called. The IRQ handler would have been called one
time more than necessary for every BRK occurred. The question here, is
which routine is called.
(It is not the first time I'm off by one, two, or three, &c., and it won't
be the last time. Ldx #$ff and ldx #5 are pretty far apart, though.)
But even an extremely simple BRK handler ---- say, to increment $d020 ---
may not be as reliable as you think it should be if the stack pointer is
presumed to be always legitimate.
Why not just put in a better ROM, say, written with
inx
inx
inx
inx
lda $0100,x
so that the bytes will always come from the stack? Surely 8 cycles isn't
that much overhead to worry about, seeing as how much incredible waste of
time is devoted to little side routines for video updating, anyway?!
As for improving the C-128, throwing out almost all of the BASIC rom's is
still a very tantalizing possibility.
--
On 5 Aug 1998, Stephen Judd wrote:
> In article <6q943k$p...@ednet2.orednet.org>,
> Matthew Montchalin <mmon...@orednet.org> wrote:
> >In a previous article, mmon...@orednet.org (Matthew Montchalin) says:
> >>
> >>Stephen Judd thinks that
> >>>>>ff4e: bd 04 01 lda 0104,x
> >>
> >>always fetches bytes from the stack!??
> >
> >One problem being that the stack includes addresses $0100-0103 --- four
> >bytes that MAY have significant information not available through lda
> >$0104,x. Of course, a BASIC programmer probably never gets around to
> >using the BRK instruction in a truly creative way, either.
>
> The question I asked was: what do you think will happen? For example, with
> the code you gave. I would like to see an example of this "sad, sad C= bug"
> in action.
>
> I do not feel that this is a complicated question, and I am still interested
> in an answer.
Here is one answer. A long answer. Not sure how much this may pertain to
Matthew's particular observations.
But anyway, I have this long document of various little observations on
code idiosyncrasies. Probably not worth putting anywhere, hard to judge
accuracy. Not written very interestingly.
Here is an extract. This is, as far as I remember, one situation where I
had observed something odd, and concerning this issue discussed above.
Don't know if I recall other examples. Maybe not.
Anyway, you might want to try out the code yourself, before reading all
below, if you like. Maybe contemplate, see what you figure out yourself
first, and why the events occur.
------------------------------------------------------------------------
;9) Whenever an interrupt occurs, of whatever type, whether it is
; maskable, non-maskable, or invoked by the BRK instruction, then
; during the interrupt sequence, before the interrupt code begins,
; the interrupt disable flag in the processor status register is
; set. Check out what happens if it was not!
; interrupt vector diverted here > CLI
; SEI
; JMP $FA65
; If you store the above code at $1300, and type '>314 0 13' from
; the 128 monitor, the computer will go berzerk! The computer will
; constantly print the break message on the screen along with its
; bell sound. You see, the original interrupt source is still
; present. It will immediately provoke another interrupt after the
; CLI instruction. Actually, the new interrupt does not occur until
; after the SEI instruction is performed. The program counter which
; is pushed onto the stack will point to the jump instruction. This
; would be true as well if SEI were replaced with some other
; instruction.
; (Note: if SEI is replaced with PHP, the break flag of the status
; register pushed onto the stack will be set - as it almost always
; is.)
; Try the following code instead:
; interrupt vector diverted here > LDA $DC0D
; LDA $D019
; STA $D019
; CLI
; NOP
; JMP $FA65
; Now everything proceeds hunky-dory like, as normal. The interrupt
; source has been removed. I would like to return to that break
; message that you saw scrolling annoyingly down the screen earlier.
; I could not make out what was causing it at first. Here's what I
; think now. When that first sample of code executed, interrupts
; were constantly occurring. Every time an interrupt occurred,
; lots of stuff got pushed onto the stack. This kept happening until
; the stack pointer underflowed, that is, it rolled over from 0 to
; $ff. That alone is not a problem. However, that left the stack
; pointer with some high value, like say for example $FC. The first
; few instructions of the interrupt code (in ROM) were executed.
; These instructions included the follwing:
;
; FF25: TSX
; FF26: LDA $0105,X
; FF29: AND # $10 ;test break bit
; FF2B: BEQ $FF30
; FF2D: JMP ($0316) ;vector to BRK rtn.
; FF30: JMP ($0314) ;vector to IRQ rtn.
;
; The actual processor status register was put on the stack, maybe
; at address $0101. But this code examines a bit from address $0201.
; Chances are 50/50 or greater that that bit will be set, and so the
; Break routine, an entrypoint into the 128 monitor, is executed
; when the system interrupt request routine should have been called.
>
> -S
>
>
>Seems at some randomish times, my computer's RESTORE key no longer
>functions. I haven't given this issue too much thought though.
Have you smacked it? (Seriously.) The RESTORE key is wired separately of
the keyboard matrix and the connecting circuitry sometimes needs a real key
thwack to get a signal.
Speaking of elaborate BRK handlers, what about using BRK from within the
ordinary IRQ handler? The routine would have to be, to some degree,
subject to reentry, and certainly ought /not/ be subject to /infinite/
reentry. One reentry after the first, ought to be enough.
-For instance, once the BRK is encountered, the following happens, in
addition to setting the BRK flag on the Processor Status register: PC
high gets pushed, PC low gets pushed, Processor Status gets pushed, and
anything after that, depends on the IRQ handler. We usually save the
registers next, by pushing them on the stack. This much would be routine,
and commonplace.
But now that everything is on the stack, you can retrieve your address by
examining the PC. -Now, at this point, grandiose things become possible,
but you do have to be careful.
For instance, suppose you want to develop a new addressing mode (cycle
consuming though it may be). Just for the sake of amusement, let's
suppose we want to emulate the BSR instruction available on other
microprocessors.
What do we do? We fetch the two bytes located after the BRK, add them to
the PC (which is already on the stack) plus two, then pull the registers
out, and RTS. Great for confounding two bit pirates armed with reverse
disassemblers. :)
--
Hmmm.
Aw, heck. That won't work. We would certainly want to pull the registers
off the stack, but we will have to first modify the PC to point to BRK+2,
and then push an artificial address onto the stack to denote the BSR
destination... Oh, well... Back to the old drawing board...
--
: >Seems at some randomish times, my computer's RESTORE key no longer
: >functions. I haven't given this issue too much thought though.
: Have you smacked it? (Seriously.) The RESTORE key is wired separately of
: the keyboard matrix and the connecting circuitry sometimes needs a real key
: thwack to get a signal.
Naw, that concerns the C64. Has a bit different circuitry (so I've read
years ago, and wouldn't understand anyway). I refer to a C128. It does
not require 'thwacking' to acknowledge RESTORE press.
The C64 does.
Pulling NMI low to make it active does not seem to do anything whatsoever
to the configuration register at $FF00. So, whether your NMI is working
properly may depend on how you have prepared the RAM under ROM at $FFFA.
So, if you are working in a non-ROM reality, and it is conceivable that NMI
could go low, be sure to have some vectors to your NMI handler set up there.
(At least, that's the way it works on my C-128.)
--
Naturally.
>in error, and you were right that your monitor program did not crash.
Good to know.
>>I do not feel that this is a complicated question, and I am still interested
>>in an answer.
>
>I doubt you are interested in any kind of answer, but how about replacing
If you are implying that I am trying to make you look foolish, perhaps
to increase my own stature, then you are wrong. I do not believe I am
needed for the former, and any fool knows that stature comes from helping
people up, not knocking them down.
Moreover, I will offer you two pieces of advice, which you may take or
leave: when you beg a question you shouldn't be surprised when someone
asks it, and humble pie is quite nourishing.
>time more than necessary for every BRK occurred. The question here, is
>which routine is called.
Progress; so far so good. In fact, the problem may be succintly summarized
in a single sentence: the ROM routines assume that the stack doesn't overflow.
So only one more question remains: do you feel this is a good assumption?
If not, can you name an existing program for which this poses a problem?
-S
In article <OfYk15Z...@nih2naab.prod2.compuserve.com>,
John Iannetta <76703...@CompuServe.COM> wrote:
> Stephen Judd said,
>
>"Obviously, there would be a problem if the stack were to overflow... but
>at that point you're totally hosed anyways.".
>
> If by "overflow", you mean pushing more than 256 bytes onto the stack, I
>disagree.
>
>$C000 LDA #$EF ; All bits set except
>$C002 STA $0203 ; bit 4
>...
>$C01D BEQ $C015 ;
>
> With SP in the forbidden range (2 to 5), the routine at $FF48 fetches the
>byte at $0203 (thinking that it's the P register). Since bit 4 is clear, the
I agree. My point is that the information that was previously on the bottom of
the stack has now been irrevocably changed, and the program is a dead duck
no matter what kind of BRK handler is used. That is, even if the handler
worked fine, once the program got back to the bottom of the stack it would
be toast.
So, sure, pedagogical examples can be constructed which spoof the handler
(or even take advantage of it!). On the other hand, the fact that such
contortions are needed are a pretty good demonstration of the robustness
of the routine, in my opinion, and I don't believe that any competent
programmer will have trouble with it.
Now, speaking of handlers -- one question that came up on the hackers mailing
list a while back was the notion of using BRK as a true software interrupt.
Has anyone out there ever used them for such a purpose? Just curious.
> And my apologies to Nicolas Welte for the capital en's; couldn't the prize
>have been a capital 3?
Well, when using the upper-case character set, the capital N is a thick
forward-slash. Maybe the prize should have been a C=-N?
-S
"Now, speaking of handlers -- one question that came up on the hackers mailing
list a while back was the notion of using BRK as a true software interrupt.
Has anyone out there ever used them for such a purpose? Just curious.".
Good to hear from you again, Steve! First of all, in that recent CMD
thread, I noticed your admonishment never to criticize a man until you've
walked a mile in his shoes. You will be happy to know, I'm sure, that I have
made that philosophy a part of my life. Now, before I criticize a man, I first
walk a mile in his shoes. Then, if he gets angry, he's a mile away and
barefoot.
The BRK opcode may be used as a software interrupt with 256 possible
signatures. My C-64 PRG lists BRK as a one-byte opcode. But my W65C816 data
sheet lists it as a two-byte opcode, and as unchanged from the NMOS 6502. The
6510 MPU (used in the C-64) increments the Program counter by 2 on the
instruction. So the IRQ/BRK handler may check the byte following the zero and
jump accordingly.
I don't know of any C-64 program that makes use of the one-byte signature.
Nor do I know of any C-64 program that does a BRK as a software interrupt. Or
do I? In some of my BASIC/ML programs, I do a:
sys2048
when I'm too lazy to clear the screen, re-install Iow-memory vectors, etc.
Miha Peternel's C-64 emulator for PC's (C64S) modifies the KERNEL ROM,
putting a BRK followed by a signature at locations where a switch must be made
to a LOAD, SAVE, etc., routine.
No. Deferring the crash for the longest period of time is the best
alternative. If you are nested very deeply, and if you wait long enough,
no matter how dangerously close to crashing you are, there may eventually
(and fortuitously) be at least one subroutine or failsafe that traps the
error and straightens out the stack pointer to some other arbitrary value.
--
Try the 1987 Commodore Show.
--
Nobody is forcing you to use the ROM routines, Steve. So, if you or
anyone wishes to do it a different way, why do you knock them? With a
C-128, it is possible to move the stack to different places in memory, and
(if it is necessary) manage two or several stacks "simultaneously." No, it is
not pedagogical to suppose wholly different ways of driving the C-128
architecture, and simply because you haven't seen it done is no reason to
believe it impossible.
--
Since none of us can attend the 1987 Commodore Show through conventional
means, why don't you elaborate?
--
Jason Compton jcom...@xnet.com
Editor-in-Chief, Amiga Report Magazine VP, Legacy Maker Inc.
http://www.cucug.org/ar/ http://www.xnet.com/~jcompton/
John> Now, before I criticize a man, I first walk a mile in his shoes.
John> Then, if he gets angry, he's a mile away and barefoot.
That was a very good one!
John> The BRK opcode may be used as a software interrupt with 256
John> possible signatures. My C-64 PRG lists BRK as a one-byte
John> opcode.
The PRG contains many other errors as well. But this is a moot point.
For the processor (any 6502 variant I know, including the 65816), BRK
is a one-byte opcode, in the sense that the processor does the same
thing (jumps to the interrupt handler), no matter what is stored in
the address followed by the BRK opcode. But it actually fetches the
byte followed by the opcode. Actually the NMOS 6502 always fetches
the two bytes for every instructions; the CMOS versions might put the
bus into an idle state or whatever during the 2nd cycle of a 1-byte
2-cycle instruction, such as ASL or NOP.
Anyway, with any 6502 variant that has an output indicating an opcode
fetch (I think that the 6502 has one, but the 6510 definitely doesn't,
and the 65816 certainly has one), you could set up extra hardware that
records the byte fetched after the BRK and dynamically changes the
interrupt vector fetched by the processor. (You could store $FF $FF
in the interrupt vector locations and force the data lines to the
correct value by the extra hardware.) Maybe the 65816 designers had
something like that in their minds.
Marko
Well, I've never finished it; moving the stack around is somewhat
costly. Maybe I'll continue playing with it someday, having a REU laying
around. Memory enough to store several stacks ;P
It can be done. (If memory serves me right, the 65816 even had a
BRK $whatever addressing mode)
--
Martijn van Buul, mart...@mud.stack.nl
Tijntje@OuterSpace - 131.155.141.166 3333
>Anyway, with any 6502 variant that has an output indicating an opcode
>fetch (I think that the 6502 has one, but the 6510 definitely doesn't,
>and the 65816 certainly has one), you could set up extra hardware that
>records the byte fetched after the BRK and dynamically changes the
>interrupt vector fetched by the processor. (You could store $FF $FF
Where would this "extra hardware" go? Would it spy on the datalines or
something?
Stinks that the 6510 doesn't have such a line though.
>Well, I've never finished it; moving the stack around is somewhat
>costly. Maybe I'll continue playing with it someday, having a REU laying
>around. Memory enough to store several stacks ;P
For all you (two or three) 65 programmers out there, the 65CE02/4510 has
opcodes for stack relocation (TBA and TAB).
Cameron> Where would this "extra hardware" go? Would it spy on the
Cameron> datalines or something?
On the data lines. It would monitor the instruction fetch line and
simultaneously see if all data lines are low (BRK opcode). Then it
would read the byte on the following cycle and write the interrupt
routine address a few cycles later (on the 6th and 7th cycle, the 1st
cycle being the BRK opcode fetch) to the data bus.
Cameron> Stinks that the 6510 doesn't have such a line though.
Yep. And it seems that I remembered wrong about the 6502 as well. At
least the 6502 drawn on the 1541 schematic diagram doesn't have any
other "exotic" pins than SO, the Set Overflow flag signal (which is
connected to the R/W head controller's "byte ready" signal).
The lack of such a line doesn't prevent building similar hardware
e.g. on the C64. The circuit would just need to be a bit more
complex. The circuit would be activated only if a byte is read after
a zero byte is read, if there are 3 consecutive writes (to the stack
page) after that, and if the next address to be read is $fffe. Well,
actually the circuit wouldn't need to compare the address lines.
Either circuit would lose an NMI if the NMI occurs in the middle of
BRK execution. In this case, the processor normally jumps to the NMI
vector with the B flag set. Maybe the circuit should also monitor the
NMI line to avoid this condition. Then the NMI handler would have to
check the B flag and "manually" call the proper BRK handler routine.
Marko
>The PRG contains many other errors as well. But this is a moot point.
>For the processor (any 6502 variant I know, including the 65816), BRK
>is a one-byte opcode, in the sense that the processor does the same
>thing (jumps to the interrupt handler), no matter what is stored in
>the address followed by the BRK opcode. But it actually fetches the
>byte followed by the opcode. Actually the NMOS 6502 always fetches
>the two bytes for every instructions; the CMOS versions might put the
>bus into an idle state or whatever during the 2nd cycle of a 1-byte
>2-cycle instruction, such as ASL or NOP.
I got this suggestion from someone I was conversing with:
"Common trick for inline parameter passing. I do it with z80s. You do a
BRK and then in the handler you get the PC from the stack and use it
to point to the address after the BRK to get that value, then the PC in
incremented by one and put back on the stack so when you do a RTI you end
up after the byte after BRK. FYI: that trick can pass several bytes
or a variable number of them that way. It's a way to fake the operation
of 9900 style XOPs or PDP-11 EMT mechanism. I've seen the 6800 SWI
(nearly identical) instruction used that way. If memory serves the
sweet16, 16 bit integer math package used that as the way to enter.
Looking at it I see lots of useful ways to use it."
Sound good to anyone? Has this been used anywhere successfully?
TBA!?
TAB!?
Never heard of...
While it's possible to change the stackpointer with TXS and TSX, it's
not very usefull. The stack is limited to the $01-page, and all you can
do is change it's startingpoint. 256 bytes isn't very much to share..
"I got this suggestion from someone I was conversing with:"
"Common trick for inline parameter passing. I do it with z80s. You do a
BRK and then in the handler you get the PC from the stack and use it
to point to the address after the BRK to get that value, then ...
"Sound good to anyone? Has this been used anywhere successfully?".
Yes, I did (just now), I THINK.
10 fori=53147toi+100:ready
20 pokei,y:next:sys53147
100 data169,166,141,22,3,169,207
110 data141,23,3,96,232,232,232,232
120 data232,188,0,1,132,251,232,188
130 data0,1,136,132,252,160,255,177
140 data251,168,240,15,136,240,29
150 data136,240,32,136,240,42,136
160 data240,50,76,102,254,186,232
170 data188,0,1,232,189,0,1,170,24
180 data32,240,255,76,188,254,32,68
190 data229,76,188,254,186,232,232
200 data232,189,0,1,32,22,231,76,188
210 data254,173,24,208,73,2,141,24
220 data208,76,188,254,162,0,240,215
Running the preceding C-64 BASIC program will install a new BREAK vector
(at $0316). The new BREAK routine supports five signatures: 0, 1, 2, 3, and 4.
In your ML program, if you follow a BRK opcode with a number from $05 to $FF,
you will get the normal warm start. But for other byte values:
$00 = Set cursor to row X, column Y (no CLC required)
$01 = Clear screen
$02 = Print byte in A
$03 = Toggle character set
$04 = Home cursor
The saving in size of an ML program boggle the mind. Instead of using a
three-byte JSR, you use a two-byte instruction. That's a saving of a whole
byte (eight bits) for each call. If your routine uses 100 such calls, that's a
saving of 100 bytes. (Do Windows programmers agonize over things like that?).
Maurice> Yes, GEOS uses a number of i_Inline calls. As for a good use
Maurice> of the BRK opcode, the 1571 uses it. While in 1571 mode, you
Maurice> can force the 71 OS to perform a job queue operation with the
Maurice> following code segment: ...
Maurice> ...
Maurice> sta jobQueue
Maurice> cli
Maurice> brk
Maurice> nop
Maurice> 50$
Maurice> lda jobQueue
Maurice> bmi 50$
Maurice> sei
Maurice> ...
Why the CLI, SEI and the waiting loop after the BRK?
Marko
Martijn> While it's possible to change the stackpointer with TXS and
Martijn> TSX, it's not very usefull.
Well, TSX can be thought as some sort of setjmp(3), and TXS as
longjmp(3). If you memorize the stack pointer with TSX, you can use
TXS to exit from a deep subroutine calling stack to the main program
level. I used this in my IRQ loader routine. While the drive is idle
(and smoothly turning the drive LED on and off using some subroutine
calls), it monitors the serial bus lines. As soon as they change, it
will do a TXS and continues with the main program. It'll even
terminate automatically if ATN is asserted.
Martijn> The stack is limited to the $01-page, and all you can do is
Martijn> change it's startingpoint. 256 bytes isn't very much to
Martijn> share..
True. On the C128 you can relocate the $0100-$01FF page (and the zero
page) using the MMU, but there must be very few programs that actually
do this.
Marko
Maurice> The loop waits for the job to finish because the interrupt
Maurice> handler doesn't necessarily run until done.
OK, so this was the reason. If the interrupt handler did everything
in one pass, only a BRK would have sufficed. BRK ignores the I flag
(and sets it, just like other interrupts (at least IRQ and NMI set it,
I'm not sure about RESET)), so the CLI and SEI would be completely
unnecessary if there wasn't a possibility that the interrupt handler
has to be called multiple times.
But then, have you ever tried something like this:
lda #job
sta queue
loop: brk
nop
lda queue
bmi loop
It should be as safe as using just one BRK in the first place.
Maurice> However, there have been very few people that have written
Maurice> 1571 code. Mostly people write code for 1541 mode.
What differences are there between these modes, other than the clock
frequency? My IRQ-loader runs perfectly well on the 1571 in both
modes, even though I wrote it for the 1541.
Marko
>>For all you (two or three) 65 programmers out there, the 65CE02/4510 has
>>opcodes for stack relocation (TBA and TAB).
>Err? All I was aware of are TXS and TSX. TBA!? TAB!?
Oops -- TAB and TBA are the wrong ones. They move *zero page* around.
Still, you can manipulate multiple 256-byte stacks on the 65CE02, since the
stack pointer is 16-bit. TSX/TXS are still there, but for moving the high
byte of the stack pointer, use TSY/TYS. So, by cleverly manipulating the high
byte, you can subdivide the stack. Woe be to you if an application gets a
little jiggy with its slice of the pie, though.
>While it's possible to change the stackpointer with TXS and TSX, it's
>not very usefull. The stack is limited to the $01-page, and all you can
>do is change it's startingpoint. 256 bytes isn't very much to share..
But 64K is plenty :-)))
> When it comes time to do something like reading or writing a
>sector, then that code fragment allows the 71 kernal to do its
>thing. That's the reason for the CLI to allow the interrupts to
>run. Then the BRK triggers the routine in the 71 kernal that
>deals with the desired job. The loop waits for the job to finish
So what happens in this code?
SEI
BRK
Does the CPU call the interrupt vector anyway even with the I flag set when it
gets to the BRK? If not, where does execution pick up?
'Yep. And it seems that I remembered wrong about the 6502 as well. At
least the 6502 drawn on the 1541 schematic diagram doesn't have any
other "exotic" pins than SO, the Set Overflow flag signal (which is
connected to the R/W head controller's "byte ready" signal).'.
Nope, you remembered correctly. The 1541 doesn't use pin 7, the SYNC pin
(an output). Referring to my 1978 Synertek Data Catalog (containing such
products as the SY4050, a 4096 X 1 Dynamic Random Access Memory that requires
+12 VDC and -5 VDC):
-------------------------------------------------------------------------------
SYNC goes high during phase 1 (corresponding to VIC's control of the
address bus with the 6510), if an op code is being fetched. The line stays low
until the end of the following phase 2.
-------------------------------------------------------------------------------
The 65C816 and 65C02, during the phase 2 after fetching a BRK opcode, hold
the R/-W line high. So the signature byte is latched into SOME register, but
is not used.
Say what???
>>The stack is limited to the $01-page, and all you can
>>do is change it's startingpoint.
Not the case for the C-128.
>256 bytes isn't very much to share..
>
>But 64K is plenty :-)))
And on the C-128, there's even more memory to play around with.
--
'Besides the speed difference, 2mhz vs. 1mhz, a completely different
circuitry is used for reading and writing to the disk. Plus in 1571
mode it's safe to mess with the overflow flag. For instance, a
simulated "branch always" can be used as follows:
clv
bvc 10$'.
First of all, many thanks for the info about "Wheels".
There really isn't any great change in the controller circuitry. The SO
pin of the 6502 is not used in 1571 modem; a line to PA7 of VIA1 is used for
"Byte ready" instead of the V flag (as you imply). The VIA2 TIMER 1 value is
halved, so the stepping motor moves the head past the tracks twice as rapidly.
Also, fast serial is supported in 1571 mode.
Maurice also said,
" clv
bvc 10$
If you do this in 1541 mode while the read/write heads are set for
writing, you'll write a byte to the disk wherever the head is
currently sitting. That's why you want to make sure you switch the
head back to read mode as soon as you're done writing to the disk.".
I don't see how a CLV will do any harm in either 1541 or 1571 mode. As far
as writing a byte to the disk is concerned, in write mode (VIA2 CA2 high and
CB2 low), head current is flowing. If the disk is spinning, GCR bytes equal to
the value in $1C01 are continually being written to disk. If the disk is at
rest, only one GCR bit can be changed. When DOS either reads or writes a GCR
byte to disk, it does something like:
CLV
HARRY BVC HARRY
LDA $1C01
CLV
So a CLV almost anywhere should go unnoticed.
"...and I'll explain why. Take the following set of instructions:
clv
bvc 10$
In the 64, the branch would always occur no matter what. But in the 41
or 71, there's a slight possibility for the overflow flag to get
set right in between the clv and bvc instructions. In that case,
whatever follows will be executed and this is not what the programmer
planned.".
I would think that for the same reason, some math routines might
not always work correctly for those times when you would want to check
the overflow flag after doing the math.".
Only an interrupt can cause what you describe, and an interrupt preserves
the flags (in the P register).
Maurice continued,
'In the "MOS HARDWARE MANUAL" that I have from 1976, it states the
SO pin is designed to work with a future I/O part and should not be
used in normal applications unless the user has programmed for the
fact the arithmetic operations also affect the overflow flag.
Certainly the 1541 wasn't thought of in 1976. I wonder if the
SO pin was used in other disk drives? Maybe the 8050 used it? Or what
I/O part did MOS have in mind for this pin when the 6502 was first
designed? Maybe the Apple computer made use of it.'.
I think that MOSTEK intended for the 6502 to be used as a general purpose
8-bit MPU. I am quite sure that the device was used by MANY manufacturers, in
games, industrial applications, home computer kits, military, etc. The SO line
might have connected to a phototransistor to indicate that a widget was passing
on a conveyor belt, or to a limit switch to indicate that a cutting tool had
moved far enough. The VIC-20 holds the SO pin high; I don't have schematics of
any other 6502 devices.
My 1978 Synertek catalog says merely that the SO pin sets the V flag on a
negative edge, and sampling is done on the trailing edge of phase 1. The 6502
also has the RDY input and the SYNC output, which may be used for slow I/O
devices, and for single-stepping through a program.
" Only an interrupt can cause what you describe, and an interrupt preserves
the flags (in the P register).".
John, you put your fingers into gear before your mind was engaged. If the
SO pin of the 6502 goes low, that will also set the V flag. But as long as bit
1 of the byte in $1C0C is clear, SO cannot go low. The DOS routines in the
1541 and in the 1571 (for both 1541 and 1571 modes) set the bit at the start of
the controller interrupt routine, and clear the bit at the end. So, assuming
that your routine is not part of the controller interrupt routine, an SEI will
let you use CLV with impunity. But with the I flag clear, each time that the
controller interrupt routine is called (about 63 times/second in 1541 mode and
125 times/second in 1571 mode), the V flag will be set every 32 useconds,
approximately (corresponding to one GCR byte).
So the V flag may be used freely (outside of the controller interrupt
routine), if the I flag is first set. But with the I flag clear, and the
controller routine being called regularly, the V flag will continually be set.
But that obtains in both 1541 AND 1571 mode.