Page 1 of 3
A small Apple I emulator (was: Another 6502 emulator...)
Posted: Fri Dec 31, 2010 5:19 am
by happycube
I've been working on a logic-based emulator - with gcc -Os it compiles into less than 4K (not counting 'shell'), which means it'll be a good choice for running on an AVR or ARM Corrtex. In theory one could make a one-chip Apple I with this on a very cheap LPC 111x.
EDIT: I haven't commented it enough yet to make sense, but it's now an Apple I emulator and can run at least basic BASIC programs. The stripped -Os i386 a.out (including basic and woz mon) is ~14K.
Check it out here:
http://code.google.com/p/chadslab/sourc ... nk%2Fm6502 (2-clause BSD licensed)
What I really want to do is make a tight Verilog version and see if I can make something smaller than even Arlet's core (EDIT: I doubt I can make one that's worth the effort, but I might look into optimizing Arlet's...)
Posted: Fri Dec 31, 2010 9:54 am
by BigEd
Nice! I'm sure an approach which takes advantage of the regularity in the instruction set - and leaves to chance(*) the behaviour of unspecified opcodes - will result in a small and tidy implementation.
I've collected a few pointers and notes to test programs
here - note that Ruud's
zip file update contains a later version of TTL6502.ASM than the one he links to
direct.
Cheers
Ed
(*) by which I mean the high level source doesn't over-specify, and the compiler or synthesis tool will do whatever costs least (hopefully.)
Re: Another 6502 emulator... (it's pretty small!)
Posted: Fri Dec 31, 2010 12:30 pm
by Arlet
What I really want to do is make a tight Verilog version and see if I can make something smaller than even Arlet's core

Do you have a similar structure in mind for the Verilog version, or do you intend to rewrite the whole design ?
Posted: Fri Dec 31, 2010 6:33 pm
by happycube
@BigEd - thanks! I actually used your page to find rb65 (I don't have an assembler for the latest version) I'm having issues with the stack stuff near the end of it and can't figure out what it's supposed to do in the first place. (This probably means BCD is still wrong

)
@arlet - yeah, aside from the natural differences in going to HW... address generation would be a bit different for instance. (edit: This assumes that I can actually wrap my head around it...)
I posted a version with some debug statements removed and it's just over 3K with 32-bit gcc's -Os, and a bit under 3K compiled for AVR. So if you don't have much ROM code and an external serial/SPI SRAM chip, this could easily work with even an 8K AVR.

Posted: Fri Dec 31, 2010 7:40 pm
by BigEd
Hmm, looking briefly at the latter part of the tests, I see that this test of Ruud's fails on a real NMOS 6502:
Code: Select all
lda #0
pha
plp
php
pla
cmp #$20
M382
bne M381
and, sadly, neither run6502 or lib65816 get the right answer either, which is that the final value of A is #$30 - is that one of your failing cases? py65 gets this one right!
(Of course, Ruud's tests are for his implementation, and it's possible that he didn't intend to model this detail. Later versions of his test suite mask this test.)
Posted: Fri Dec 31, 2010 7:49 pm
by Arlet
My 6502 model doesn't get this right either. I only set bit 4 when the P register is pushed in a BRK, but not during a regular PHP. Bit 5 is never set.
Both are easy to fix, if anybody cares.
Posted: Fri Dec 31, 2010 7:54 pm
by BigEd
The nice thing about fixing these little things is that the same tests can run and pass on all models. Probably these niggles wouldn't break software, and they must be very low on the priority list.
The rule on B is that it's always written as a 1, unless it's being written as a response to IRQ(*). This is because there's no storage for it, just a pull-down(**) which is generally inactive, and so generally it behaves like the unused bit.
Cheers
Ed
(*) aargh, see later: I may not have this right. It's right that there's no storage, but the exact situations which affect the pushed value might not be as I say. I'll re-edit when I know more. My earlier test was on a real NMOS 6502 using PHP/PLP, so the result for that specific case remains valid.
(**) aargh, not a pulldown, a bit more subtle than that.
Posted: Fri Dec 31, 2010 8:18 pm
by kc5tja
BigEd, perhaps you can answer this with the Visual6502 stuff you've done. Since most hardware will hold the -IRQ line in the asserted state until the source of the interrupt has been addressed, it's easy to see that B can just reflect the state of the -IRQ line as-is. I wonder, though, what will happen if the -IRQ line is asserted just long enough for the CPU to register an interrupt, and negated afterwards?
That is, if IRQ is treated like ABORT or NMI, will B continue to be 0 when the flags are pushed? I'm wondering this because when the flags are pushed, -IRQ will be high again.
Posted: Fri Dec 31, 2010 8:48 pm
by BigEd
The quick answer is that there are several bits of state on the way from IRQ the pin to the B bit pulldown, so I'd very much expect the right thing to happen - it's not a direct connection, but a saved status (which is also subject to RDY stalling the pipeline)
The longer answer is that I'm not sure, and I've lost confidence in my earlier answer! I'm not even sure if visual6502 is behaving correctly, which would be very interesting indeed. URLs like these are what's needed to explore this question:
http://visual6502.org/JSSim/expert.html ... ,DPControl
where
- a and d are the address and data for a test program
- irq0 and irq1 are the half-cycles in which IRQ is changed to 0 then 1
I'll have to look at this more deeply when I have a slightly clearer head, with reference to this
tutorial and also this statement
software instructions BRK & PHP will push the B flag as being 1.
hardware interrupts IRQ & NMI will push the B flag as being 0.
made elsewhere.
It's a shame that I've added confusion instead of clarity, to an area where confusion is easily come by.
Cheers
Ed
Edit: silly me: I was trying to test IRQ without first using CLI, so I was never IRQing, just falling off the end of my program into a BRK.
Posted: Fri Dec 31, 2010 10:20 pm
by Arlet
Ok, I've fixed my model's handling of the B flag.
Posted: Sat Jan 01, 2011 12:04 am
by happycube
Yup, that's where I was stopped - thanks for the tips, that got me moving again and now the entire test passes! (it's 3181 bytes now...)
Posted: Sat Jan 01, 2011 12:04 am
by ElEctric_EyE
Nice! Arlet... And you fixed decimal mode too?...
Thank God for forums!
Posted: Sat Jan 01, 2011 12:09 am
by Arlet
Yes, decimal mode is also implemented. See this post and the replies:
viewtopic.php?p=12534#12534
Posted: Sat Jan 01, 2011 6:03 am
by BigEd
OK, I've found my silly error: I was running the visual6502 simulator and thought I was testing IRQ, but hadn't used CLI so in fact I was running off the end of my test program and actually seeing BRK.
Short answer again: B is affected by the pending interrupt pipeline control signal, not by the 'live' input from the IRQ pin.
Here's an URL which uses CLI and sets off a very short IRQ pulse:
http://visual6502.org/JSSim/expert.html ... ,DPControl
You'll see that the
D1x1 signal (as named by Balazs in his
giant schematic) latches the pending interrupt, causes the pushed B to be zero, and is then cleared during the vector pull. This same signal is gated by 'Fetch' to produce 'ClearIR' which is what causes the sequencer to act as if it just fetched a BRK in handling any of the cases IRQ, NMI, RESET.
Note that the visual6502 sim reports the P register as if B was a storage element: in fact it is observing the node which conditionally drives the data bus during a push of P. See here:
http://visual6502.org/JSSim/expert.html ... &zoom=10.7
This node is the output of an inverter (I was wrong about the pulldown), and is a doubly-inverted D1x1. There are only 6 storage elements written by PLP.
Brad Taylor's statements:
software instructions BRK & PHP will push the B flag as being 1.
hardware interrupts IRQ & NMI will push the B flag as being 0.
which I quoted earlier are now borne out by what I see in visual6502. I notice that RESET also uses D1x1 and therefore would push a zero for the B flag, except in the NMOS 6502 the three pushes in this case are masked out (as
discussed previously) - I imagine that the 65C02 will push with B as 0.
Cheers
Ed
(Edit: link to production version as it has been updated)
Posted: Sat Jan 01, 2011 6:21 am
by BigEd
Brad Taylor's statements:
software instructions BRK & PHP will push the B flag as being 1.
hardware interrupts IRQ & NMI will push the B flag as being 0.
Except,
it seems, in another case: if an NMI interrupts a BRK, D1x1 does not register a pending interrupt and B is pushed as 1. The vector used is the NMI vector. So, if an NMI handler inspects the pushed P it will normally see B as 0, but not always.