Using a 2A03 by itself + programming

Discuss technical or other issues relating to programming the Nintendo Entertainment System, Famicom, or compatible systems. See the NESdev wiki for more information.

Moderator: Moderators

skrasms
Posts: 45
Joined: Mon Jan 14, 2008 7:20 pm
Location: Portland, OR

Using a 2A03 by itself + programming

Post by skrasms »

Hello everyone, this is my first post here. I am working on a project that will interface a 2A03 with a microcontroller to make a synthesizer module. It will bring together what I already know about analog circuits and hopefully give me some new skills on the digital side.

Right now my biggest problem is figuring out what data I need to send to the 2A03 to get the audio up and running without any other chips (except a microcontroller) present. I have never worked with a bare CPU before. I have read over the addresses and data values to work with the sound channels, but what else is necessary? Does the 2A03 need specific instructions to be initialized? Once it is running is it just a matter of writing new data bits to the audio channel addresses?

If I can get the software figured out then I should be good to go. I'm an electrical engineer, but not a computer engineer. If anyone can help me it would be greatly appreciated. If anyone is interested I can try to document the project as it progresses.
tepples
Posts: 22915
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Post by tepples »

The 2A03 is just a 6502 family CPU with different I/O and no decimal mode. As with any 6502 family CPU, you need ROM in at least the upper 256 bytes or so of address space ($FF00-$FFFF), in order to have somewhere to put the reset and interrupt vectors. If you want to actually do anything, you need RAM in at least the lower 512 bytes ($0000-$01FF).

But you don't even need the upper address lines connected to anything. Notably, the Atari 2600 leaves A13 through A15 disconnected,[1] mapping the ROM into $1000, $3000, $5000, $7000, $9000, $B000, $D000, and $F000. It also puts I/O into $0000-$007F and maps the 128 byte RAM into both $0080-$00FF and $0180-$01FF.


[1] The 6502 variant used by the 2600 actually comes in a package that doesn't even have pins for A13-A15, /IRQ, /NMI, and a few other 6502 functions. Pins make up a significant fraction of the price of a chip.
User avatar
Bregalad
Posts: 8157
Joined: Fri Nov 12, 2004 2:49 pm
Location: Divonne-les-bains, France

Post by Bregalad »

What you mention isn't possible unless you are able to simulate ROM with the microcontroller or something, wich doesn't much sound like doable.
The 2A03 is NOT a sound chip, it is a microprocessor with a built-in sound chip. There is NO I/O pins that allow you to directly write to the sound regs ($4000-$4015), instead you should have a programm that is read through the microprocessor's data and adress I/O, and that programm should write to it.
In the best case your programm would be something utterly simple like an instruction that constantly jump on itself, and then you replace it by a sound register write when you want to write to it before coming back to your instruction that jumps on itself. So you will need at least 8 bytes of RAM (or pseudo-RAM emulated by something), 2 for "LDA #$xx" (where xx can be modified by the microcontroller), 3 for "STA $40yy" (where yy can also be modified by the microcontroller), and 3 for "JMP $fffb". Assuming you map those bytes at $fff6 to $fffd, the reset vector will automatically point on $fffb, wich is the instruction jmp $fffb. When the microcontroller wants to write to a sound register, it have to first set the xx data and yy adress to its needed values, then replace the $fb in "jmp $fffb" per a $f6, and then almost immediately set it to $fd again to avoid multiple writes.

Another (and probably better) solution would be to have a small RAM chip (say 8 KB) that is mapper in 6502 memory map, and have a more complex programm running here. I don't know how the main microcontroller could write to it tough.
skrasms
Posts: 45
Joined: Mon Jan 14, 2008 7:20 pm
Location: Portland, OR

Post by skrasms »

Bregalad wrote: There is NO I/O pins that allow you to directly write to the sound regs ($4000-$4015), instead you should have a programm that is read through the microprocessor's data and adress I/O, and that programm should write to it.
I'm having some problems understanding the processor, I think. Please bear with my ignorance. My original thought was that using the microcontroller I could set the 2A03's address bus pins to the address I want to write (such as $4007), the data bus pins to the data I want to write (such as $F8), enable pin 34 for writing, and that data would go into that address at the next clock cycle or so. By having the microcontroller write different data to different addresses (it is actually reading in analog voltages and converting them to digital - this part is done) I could update frequency values, duty cycles, etc. Is it not possible to do that simply, putting the bulk of the work into the microcontroller's program? Do I have to instead write a program to the 2A03 and then have the microcontroller constantly update that program's code?
User avatar
Bregalad
Posts: 8157
Joined: Fri Nov 12, 2004 2:49 pm
Location: Divonne-les-bains, France

Post by Bregalad »

No, no, no ! Adress pins are OUTPUT on a microprocessor, as well as R/W ! You could do this only if the sound chip was on the outside of the processor, and it's not.
skrasms
Posts: 45
Joined: Mon Jan 14, 2008 7:20 pm
Location: Portland, OR

Post by skrasms »

Thank you for clearing that up! I've been working with Atari Pokey chips too, and on those the address pins are inputs so I mistakenly thought everything worked that way.

So now I need to learn how to get code to the 2A03, or how to make the 2A03 execute code. I imagine this stuff is pretty basic, so do you have a link or anything where I could get started reading? I am trying to learn as much as I can.

Thank you for helping me.
tepples
Posts: 22915
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Post by tepples »

To use the 2A03 like a traditional discrete sound chip, you can have some glue logic simulate an 8-byte ROM. I think it'd look like this, where the operand of JMP overlaps the reset vector:

$FFF6: $A0 (LDY #$xx)
$FFF7: data value
$FFF8: $8C (STY $40xx)
$FFF9: address value
$FFFA: $40 (high byte of address)
[here the 2A03 waits a cycle to complete the read)
$FFFB: $4C (JMP $FFF6)
$FFFC: $F6
$FFFD: $FF

As for how to get the 2A03 to execute code in the traditional fashion, you could look up 6502 hardware projects.
User avatar
blargg
Posts: 3715
Joined: Mon Sep 27, 2004 8:33 am
Location: Central Texas, USA
Contact:

Post by blargg »

Have something which feeds data to the 2A03 as memory reads. All it needs to feed is $A2 xx $8C yy $40. This corresponds to LDY #xx, STY $40yy. Then your circuit could just keep asserting NMI every 6+7 clocks. It would need to feed some data for the NMI vector read, but that wouldn't need to be anything specific as long as the values didn't fall within I/O space. I bet a pretty minimal circuit could feed this fake instruction stream to the 2A03.
skrasms
Posts: 45
Joined: Mon Jan 14, 2008 7:20 pm
Location: Portland, OR

Post by skrasms »

Yesterday a parts order from Digikey came in, so I now have a 2A03 powered and clocked. If I check the address bus pins after it's been running, they read $FFFF. Does that mean the chip has already stepped through all the address values? When it turns on does it just count up through the address values until it reaches the end or gets an instruction to do otherwise?

If I do the glue logic for an 8-byte ROM, is there a special reason to trigger the data starting at $FFF6 ? A table I found online shows program ROM from $8000-$FFFF, does that mean that at all those address values the 2A03 is potentially taking in instructions/etc on the data pins?

I've been reading about how the NMI works here (http://www.6502.org/tutorials/interrupts.html), but I need more clarity. When I trigger an NMI, the program jumps to the address location set by the NMI vector, is that right? At that address is it only executing program data already on the chip, or can it read instructions from the data pins? How do I set the NMI vector?

Again, thanks to everyone for helping me. I'm sure these are beginner questions.
tepples
Posts: 22915
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Post by tepples »

skrasms wrote:If I do the glue logic for an 8-byte ROM, is there a special reason to trigger the data starting at $FFF6 ?
One of the first things that any 6502 does when powering up is read an address from the reset vector, and then jump (set the program counter) to that address. This reset vector happens to be located at $FFFC and $FFFD, which is ordinarily ROM. I chose $FFF6 so that the JMP instruction to handle the next byte would overlap the reset vector.
A table I found online shows program ROM from $8000-$FFFF, does that mean that at all those address values the 2A03 is potentially taking in instructions/etc on the data pins?
The 6502 family can execute code from any address, except possibly an on-chip I/O area. The 6510 in Commodore 64 computers has an I/O area, and so does the 2A03. Any other ROM or RAM address can contain code. (It's possible to jump to the I/O area, but here be dragons.)
When I trigger an NMI, the program jumps to the address location set by the NMI vector, is that right? At that address is it only executing program data already on the chip, or can it read instructions from the data pins?
No program data is stored on the 2A03 chip.
How do I set the NMI vector?
By putting values into $FFFA-$FFFB, which is ordinarily ROM. In the case of glue logic faking the presence of a ROM, this involves putting the low byte on the data bus when the value on the address bus is $FFFA, and putting the high byte on the data bus when the address is $FFFB.
User avatar
Bregalad
Posts: 8157
Joined: Fri Nov 12, 2004 2:49 pm
Location: Divonne-les-bains, France

Post by Bregalad »

Code on the NES is usually $8000-$ffff because it's where its the most easier to map. It is necessary to have at least 6 bytes of ROM at $fffa-$ffff for the NMI, reset and IRQ vectors. (NMI and IRQ aren't required if you don't use those interrupts, any NES game will have to use NMI, but technically nobody forces you to use it, on a hardware project if you put NMI and IRQ pin high (with a coupling cap) you will have no problem).

It is possible to map ROM anywhere on the NES, but you don't want it to conflict with anything else. Since $4000-$4014 are write only and (correct me if I'm wrong) returns open bus, it is possible to map ROM here (not RAM tough). $4015-$4017 are read/write, so you cannot map ROM here because this will conflict (altrough not all bits are used). It is possible to map ROM to $4018-$7ffff as well as $8000-$ffff with proper hardware (unless $4015-$4017 mirrors somewhere else, I don't remember in fact).

The chip itself does not know what hardware is arround him, so if you power a 2A03 alone it will read from the reset vector, read open bus (most likely FFFF) and will jump to $ffff, read it, found $ff and since this is no valid instruction it will freeze or something. Prior to this the chip should try to write to $1ff and $1fe or something, phushing it's older adress and processor status as reset is also an interrupt. If you didn't connect /IRQ and /NMI to high, I'd be afraid things are much worse.
User avatar
kevtris
Posts: 504
Joined: Sat Oct 29, 2005 2:09 am
Location: Indianapolis
Contact:

Re: Using a 2A03 by itself + programming

Post by kevtris »

skrasms wrote:Hello everyone, this is my first post here. I am working on a project that will interface a 2A03 with a microcontroller to make a synthesizer module. It will bring together what I already know about analog circuits and hopefully give me some new skills on the digital side.

Right now my biggest problem is figuring out what data I need to send to the 2A03 to get the audio up and running without any other chips (except a microcontroller) present. I have never worked with a bare CPU before. I have read over the addresses and data values to work with the sound channels, but what else is necessary? Does the 2A03 need specific instructions to be initialized? Once it is running is it just a matter of writing new data bits to the audio channel addresses?

If I can get the software figured out then I should be good to go. I'm an electrical engineer, but not a computer engineer. If anyone can help me it would be greatly appreciated. If anyone is interested I can try to document the project as it progresses.
Welll, you're in luck. I worked out a small TTL schematic that will do the trick. It involves no RAMs and no ROMs, and I doubt you could make it any smaller unless you used a CPLD, PEEL, PLD, or FPGA.

It's 6 chips.

The pic is here:

http://kevtris.org/IMG_2306.JPG


How it works:

The above circuit forms some opcodes that the CPU will execute, as well as stuffing in some bytes for the opcodes. Everything is mapped into memory over and over, every 8 bytes.

To the CPU, it looks like this:

0000: A0
0001: <data byte from latch>
0002: 8C
0003:<address byte from latch>
0004: 40
0005: 00
0006: 00
0007: 00

So, on reset, the CPU fetches data from FFFC and FFFD and will pull the reset vector 0040h (since it's coming from addresses 4 and 5).

This will get the ball rolling, and the CPU will perform the following three opcodes:

0040: LDY #<data byte>
0042: STY 040<address byte>
0045: BRK

the BRK then fetches the address from 6 and 7 which points to 0000h.

0000: LDY #<data byte>
0002: STY 040<address byte>
0005: BRK

and the cycle repeats, over and over again. Your microcontroller or whatever can simply monitor one of the read toggles on one of the latches to figure out when the chip has performed the write. This will allow for single write precision. When you are done writing, simply set the address to some non-sound register value such as 03fh or 0ffh or something. Technically, you only need the lower 5 bits of the address, and you can hardwire the upper 3... Then, address 01fh can be used for "no write", since there's no register sitting at 0401fh.

* * * * *

I dunno if it was mentioned already, but why don't you just use the 2A03 itself as your CPU? It's got more than enough horsepower for what you're wanting to do most likely.
/* this is a comment */
User avatar
Bregalad
Posts: 8157
Joined: Fri Nov 12, 2004 2:49 pm
Location: Divonne-les-bains, France

Post by Bregalad »

My, it's amazing how you got something that small and that smart.

And the guy probably doesn't know about programming 6502, nor does he want external ROM/RAM on the circuit.
skrasms
Posts: 45
Joined: Mon Jan 14, 2008 7:20 pm
Location: Portland, OR

Post by skrasms »

Thank you very much for that detailed post, kevtris!

The reason I'm using a microcontroller is because I need a minimum of 6 channels A/D (pitch and volume for each channel), in addition to reading in several rotary encoders. There is also a lot of math in the pitch mapping that I need, and I would not know where to begin without being able to code that in C.

Your schematic looks great. I think I understand the process, but I'll need some more time to digest the whole thing :)

By the way, you're in Indianapolis? That isn't so far from me up here at Purdue West Lafayette.
User avatar
blargg
Posts: 3715
Joined: Mon Sep 27, 2004 8:33 am
Location: Central Texas, USA
Contact:

Post by blargg »

That "discrete ROM" approach is really simple. The '138 decodes the address into 8 individual enable lines, each of which "enable" that byte of the ROM. The two data values you provide are simple. The other values are gated by the remaining '245, and generated by a clever combination of gates (and choice of which opcodes to use).
Post Reply