6502.org Forum  Projects  Code  Documents  Tools  Forum
It is currently Sat Nov 23, 2024 11:48 pm

All times are UTC




Post new topic Reply to topic  [ 22 posts ]  Go to page 1, 2  Next
Author Message
PostPosted: Wed Oct 09, 2013 1:01 am 
Offline

Joined: Tue Oct 08, 2013 5:40 am
Posts: 72
Location: /home/sci4me
Hi guys. First, hope ya'll don't mind my large amount of topics but well.. anyway, if you don't know by now, I am working on designing a "simple" processor which is a bit of a ramped up version of the 6502. So, as a bit of background, if any of you play minecraft and play with mods, you may have heard of a mod called RedPower2. This mod added its own custom processor called the 65EL02 which is a 6502 and a few other processors together. In the code for the emulator, there is a fairly simple system for the processor to communicate with hardware devices. In my emulator I could easily create something like this however as far as hardware goes I am a bit clueless. My main question is how would I go about implementing some sort of hardware communication system? Like I said, I could EASILY create a system in the emulator but as far as the hardware (real hardware not virtual) goes, I'm not sure at all...


Top
 Profile  
Reply with quote  
PostPosted: Wed Oct 09, 2013 3:29 am 
Online
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8546
Location: Southern California
The most common I/O IC to connect to a 6502 is the 6522 (or preferably, the 65c22) VIA. It lives up to its name, "Versatile Interface Adapter."
  • It has two 8-bit parallel ports
  • with additional read & write hardware handshake lines you can optionally use for something like a printer port,
  • each bit of the parallel ports is individually programmable to be input or output,
  • it has two timer/counters with lots of modes of operation,
  • a synchronous-serial port with seven modes of operation,
  • and seven sources of interrupts (individually enableable),
  • including negative or positive active edges on particular lines.

I've use the synchronous-serial port for many things including a 9-level PWM which was quite adequate for generating DTMF (for phone tone pairs for dialing) and even voice audio without a D/A converter. I have a lot of tips on things you can do with this IC in my Tip of the Day topic, and in the 6502 primer, particularly in the circuit potpourri page. The best way to learn the VIA is just to read the data sheet.

The next most common one to interface to a 6502 might be the 6551 (or preferably the 65c51) ACIA, or Asynchronous Communications Interface Adapter (basically the same as a UART, or Universal Asynchronous Reciever/Transmitter), which is commonly used for RS-232 (TIA-232) or similar. The NMOS one had a bug, or maybe just a dumb design feature, that was corrected in the CMOS version, so I recommend the latter. I have used it with no problems, including for MIDI (musical instrument digital interface) which runs at 31.25kbps [Edit, 6/12/20: and more recently used it at 115.2kbps]. There are other popular UARTs like the 16550, the 2692, and one I have exercised but not put into actual service, the 14-pin-DIP SPI-interfaced MAX3100.

You'll see in my links above that I've bit-banged SPI on the VIA which is very easy but if you want maximum speed with less processor overhead you can use Daryl's 65SPI chip.

My I/O ICs page lists many others, most of which are no longer in production; but there are ways to interface almost anything on the market. The displays page mentions some video & display options, and for anything that doesn't need absolute maximum speed, you can go through the VIA for SPI or I²C and do almost anything, as there are thousands of ICs on the market available with those interfaces to do more functions than you can shake a stick at.

_________________
http://WilsonMinesCo.com/ lots of 6502 resources
The "second front page" is http://wilsonminesco.com/links.html .
What's an additional VIA among friends, anyhow?


Top
 Profile  
Reply with quote  
PostPosted: Wed Oct 09, 2013 4:18 am 
Offline

Joined: Sat Dec 13, 2003 3:37 pm
Posts: 1004
sci4me wrote:
My main question is how would I go about implementing some sort of hardware communication system? Like I said, I could EASILY create a system in the emulator but as far as the hardware (real hardware not virtual) goes, I'm not sure at all...

Garth's summary is useful, but not sure if it's helpful to you.

The 6502 family (among others) uses Memory Mapped I/O. That is, is treats I/O just like it does memory. If you want to determine the values of something (like switches), you read a byte. If you want to change the values (like LEDs), you write a byte.

In contrast, the Intel family of processor historically have specialized IN and OUT instructions, and control pins on the CPU to drive I/O devices.

So, on a 6502, if you wanted to read the value of a serial port, you'd just do something like LDA $1234, where $1234 is the address of the serial port. On an Intel device, you would do something like IN $12, which would read device mapped to $12.

The chips that Garth was referencing are the devices that turn those "memory" references in to real work. For assorted reasons, you don't want to just hang I/O things (like switches and LEDs) straight on the address and data bus, so there are other chips that make that much easier and simpler.

As far as implementing I/O in a simulator, it's quite simple. You have code in your low level memory read and write routines and then handle the I/O. You can do this simply, on my simulator, I have a byte mapped for reading the keyboard and one for displaying a character on the terminal. When the simulator does a STA $C001, my memory write routine checks for the special address and simply calls the internal "display character" routine, that handles the screen address, scrolling, etc.

Normally, a memory write is simply setting an array value: memory[address] = value. When the special address is encountered, it calls the appropriate routine. A downside of my technique is that from a clock time perspective, writes to the character output byte take "longer". The CPU increments the correct number of cycles, but actual, wall clock time, is much longer than setting an array value, it does a lot more actual work in the simulator.

This is fine for my purposes, I don't have strict timing, but you can see that if you did, you'd have to compensate for that somehow.

I also have Disk I/O in the same way. Put the start address in two special bytes of memory, set the disk block number in two other bytes (65536 total blocks), and then store a 1 or a 2 in to the control byte, and, magically a 512 byte block of memory is written to disk, or read from it. Super simple.

Here's the fragment from my Forth:
Code:
0270 c000              CHAR_READY = $C000
0271 c001              CHAR_IN = $C001
0272 c002              CHAR_OUT = $C002
0273 030f             
0274 030f  ad 00 c0    GETKEY    LDA CHAR_READY
0275 0312  f0 fb                 BEQ GETKEY
0276 0314  ad 01 c0              LDA CHAR_IN
0277 0317  60                    RTS
0278 0318             
0279 0318  8d 02 c0    OUTCHAR   STA CHAR_OUT
0280 031b  60                    RTS

The CHAR_READY byte is set to 1 if there is an unread key, then CHAR_IN is called to read the key.

Here's the Forth using the Disk I/O
Code:
3403 1715              ;
3404 1715              ;         Disk Control addresses
3405 1715              ;
3406 c003              DSK_SEC_LO    = $c003   ; Low byte of disk sector
3407 c004              DSK_SEC_HI    = $c004   ; High byte
3408 c005              DSK_SEC_COUNT = $c005   ; Number of sectors to operate on
3409 c006              DSK_ADDR_LO   = $c006   ; Low byte of memory address
3410 c007              DSK_ADDR_HI   = $c007   ; High byte
3411 c008              DSK_RESULT    = $c008   ; Operation result code
3412 c009              DSK_MODE      = $c009   ; Operation mode: 1=READ, 2 = WRITE
3413 c00a              DSK_CTL       = $c00a   ; Write non-zero to start operation

3451 1763              ;: R/W ( addr sec f )
3452 1763              ;     0= IF 2 ELSE 1 THEN R> ( Push read flag on to return stack )
3453 1763              ;     DSK_SEC_LO ! ( store sector, no need to range check -- 0-65535 )
3454 1763              ;     DSK_ADDR_LO ! ( store buffer )
3455 1763              ;     R> DSK_MODE C! ( store r/w flag )
3456 1763              ;     1 DSK_SEC_COUNT C! ( 1 sector )
3457 1763              ;     1 DSK_CTL C! ( perform transfer )
3458 1763              ;     DSK_RESULT C@ 1- 8 ?ERROR;

This was nice because my simulator does all the heavy lifting, stuffing numbers in to memory was easy to do in high level Forth.

Why did I pick this scheme for Disk I/O? Seemed like the thing to do at the time, it's not based on anything. Why did I map my I/O at $C000? Same reason.

Now you can make your simulator more accurate, do interrupt processing for I/O, etc. I haven't. My needs haven't been that strict. But you can certainly go there.


Top
 Profile  
Reply with quote  
PostPosted: Wed Oct 09, 2013 4:27 am 
Offline

Joined: Tue Oct 08, 2013 5:40 am
Posts: 72
Location: /home/sci4me
So now I need to try to understand how the mapping works... In my simulator, I have an (java stuff ahead) interface called IRAM which has ramRead, ramWrite and reset in it, this is the memory i use inside the processor, is this also where i would do the peripheral interface? If so then it shouldn't be named IRAM any more but more like IBus or something like that right?

And by the way, thanks for the responses, very informative!


Top
 Profile  
Reply with quote  
PostPosted: Wed Oct 09, 2013 4:53 am 
Offline

Joined: Sat Dec 13, 2003 3:37 pm
Posts: 1004
sci4me wrote:
So now I need to try to understand how the mapping works... In my simulator, I have an (java stuff ahead) interface called IRAM which has ramRead, ramWrite and reset in it, this is the memory i use inside the processor, is this also where i would do the peripheral interface? If so then it shouldn't be named IRAM any more but more like IBus or something like that right?

On mine, I have a very simple interface:
Code:
public interface Memory {
    public int fetchByte(int addr);
    public void putByte(int addr, int value);
    public int fetchWord(int addr);
    public void putWord(int addr, int value);
}

Then I have a couple of implementations, one of which is TermMemory. Here is it's fetchByte routine:
Code:
    public int fetchByte(int addr) {
        if (addr == CHAR_READY) {
            return terminal.hasKey() ? 1 : 0;
        }
        if (addr == CHAR_DATA_IN) {
            return terminal.getKey();
        }
        return ram[addr];
    }

Any time I want to make new memory mapped I/O, I can simply implement my Memory interface and plug something else in.


Top
 Profile  
Reply with quote  
PostPosted: Wed Oct 09, 2013 4:59 am 
Offline

Joined: Tue Oct 08, 2013 5:40 am
Posts: 72
Location: /home/sci4me
Okay, thats similar to mine I suppose. I think I got it... btw is your simulator OS? If so, gimme XD for reference.. also I am interested in playing with it. But I still have the question (which may be stupid of me at this point) of how you actually map the device to the processor.. how you tell the processor how to talk to the device.. The way it was done in the 65EL02 is basically each device has an ID and it used a MMU to map the id to the actual device and send commands to it.. is this a realistic way to implement it? If so i'll just do it that way..


Top
 Profile  
Reply with quote  
PostPosted: Wed Oct 09, 2013 6:18 am 
Offline

Joined: Mon Mar 25, 2013 9:26 pm
Posts: 183
Location: Germany
sci4me wrote:
But I still have the question (which may be stupid of me at this point) of how you actually map the device to the processor.. how you tell the processor how to talk to the device..

On a typical 6502 system all devices, the processor is talking directly to, are memory mapped. All devices like the 6522 VIA or 651 ACIA or even a simple 74HC573 latch chip have at least one "chip-select" line that is responsible for activating the chip if the correct address is set to the address bus. Because this means, that the data on the databus is for exactly this device. What you need is some kind of address-decoding that generates the chip-select signal for your several support-chips.
Once you have a chip on the data- and address-bus you can use it to communicate to other hardware "indirectly" by using f.i a VIA I/O to drive a LCD Display.

One example: I'm playing currently with are 74HC574 octal D-flipflop chips. They can be easily used as input and output device for 8 I/O lines.
These chips have 8 input-lines that I connected directly to the data bus. The 8 output-lines are driving 8 LEDs for a visual effect. The 74HC574 has also a /OE line, which is called "Output Enable". The "/" before the name is important, because it tells you that this line is "low" active. This means if the line is pulled down to GND the chip is active, otherwise not. The chip has also a "LE" Input (Latch enable) that is high-active. If this input gets an LOW-to-HIGH change it takes the data from the 8 input lines and present it on the output lines (permanently as long as the /OE line is LOW).
To talk to my 74HC574 as device on the bus I use my address-decoding logic that generates the "LOW-HIGH-LOW" pulse for the LE input of my device, whenever the address $a000 is set to the address-bus.
So my 65C02 does not now about this special device, it only writes a byte to the specific address $a000 and the bit-pattern of the bytes shoes up on the output lines of the 74HC574, because the chip is activated by the address $a000.
For all other memory mapped chips its the same. You generate a Chip-enable signal for the peripheral chip to activate it for specific addresses. Then the chip itself is responsible for reading or writing data to the databus.
This is really simple and clever, because you do not need special I/O commands to hook almost any device that supports "chip-enable" to your MPU.

For your emulator you can create a "base class" the provides that simple mechanism. It could "register" itself on your address-bus and whenever a byte is read or written to a specific address an instance of this device has registered to, you can delegate this read/write to this instance. This follows the hardware concept straight forward. From the base-class you can then derive any I/O device you'd like to implement.

Mario.

_________________
How should I know what I think, until I hear what I've said.


Top
 Profile  
Reply with quote  
PostPosted: Wed Oct 09, 2013 7:58 am 
Online
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8546
Location: Southern California
Quote:
I still have the question (which may be stupid of me at this point) of how you actually map the device to the processor. How you tell the processor how to talk to the device.

I'll add another example of register addresses beyond what's shown in my address-decoding page for I/O IC addresses. As the others pointed out, the 6502 uses memory-mapped I/O. So suppose you have a VIA whose base address is at $6000. It has 16 registers, and they will be at addresses $6000 to $600F in the memory map, and as far as the processor is concerned, they might as well be memory. It doesn't care. It just does its job, and the VIA does its job, and things happen as they should. Your code will include something like:

Code:
VIA1:         EQU  $6000   ; Base address of VIA 1

VIA1_PB:      EQU  $6000   ; port B
VIA1_PA:      EQU  $6001   ; port A
VIA1_DDRB:    EQU  $6002   ; data direction register B
VIA1_DDRA:    EQU  $6003   ; data direction register A
VIA1_T1CL:    EQU  $6004   ; timer 1 counter low  byte
VIA1_T1CH:    EQU  $6005   ; timer 1 counter high byte
VIA1_T1LL:    EQU  $6006   ; timer 1  latch  low  byte
VIA1_T1LH:    EQU  $6007   ; timer 1  latch  high byte
VIA1_T2CL:    EQU  $6008   ; timer 2 counter low  byte
VIA1_T2CH:    EQU  $6009   ; timer 2 counter high byte
VIA1_SR:      EQU  $600A   ; shift register
VIA1_ACR:     EQU  $600B   ; auxiliary  control register
VIA1_PCR:     EQU  $600C   ; peripheral control register
VIA1_IFR:     EQU  $600D   ; interrupt  flag  register
VIA1_IER:     EQU  $600E   ; interrupt enable register
VIA1_PANOHS:  EQU  $600F   ; port A, but with no handhshaking


So let's say you want to set up port B for all outputs except bits 7 and 6. You would do:

Code:
        LDA  #00111111B
        STA  VIA1_DDRB


Then suppose you want to set the output bits to 010110B. You might have relay drivers, lights, audio muting, or something else connected to each bit. You could do:

Code:
        LDA  #010110B
        STA  VIA1_PB

You just wrote zeroes to bits 7 and 6, but it will have no effect at this time since they are inputs, not outputs. If you read them, they will show what's being input from the pins, not what you just wrote to the port. (If/when you change those bits to outputs, then they will take on the values you wrote to them.)

If you had a relay driver connected to bit 0 and a high output would actuate the relay and that's what you wanted to do, you could do:

Code:
        LDA  VIA1_PB
        ORA  #1
        STA  VIA1_PB

or, if you knew for sure that bit 0 was already a 0, you just increment it to a 1 without affecting the other bits:

Code:
        INC  VIA1_PB

The data-direction register turns out to be a nice thing for the equivalent of an open-drain output, which is how I do the I²C interface. You just leave a 0 in the output latch, pull it up with a resistor from the outside, and then change the bit's direction by writing to the data-direction register (DDRA or DDRB). When you make it an output, it pulls it low. When it's an input, you can read it to find out if something else is pulling the line low, or if the pull-up resistor is being allowed to pull it up to Vcc.

That of course only addresses the two parallel ports and their data-direction registers; but the addressing works the same way for the other registers too. (The operation is slightly different on the interrupt-enable register, so you can enable or disable an interrupt source without reading and ANDing or ORing a bit in or out.)

There's a load of applications, interfaces to real-world circuits, plus in many cases code to go with it, on the circuit potpourri page of the 6502 primer.

_________________
http://WilsonMinesCo.com/ lots of 6502 resources
The "second front page" is http://wilsonminesco.com/links.html .
What's an additional VIA among friends, anyhow?


Top
 Profile  
Reply with quote  
PostPosted: Wed Oct 09, 2013 12:16 pm 
Offline

Joined: Tue Oct 08, 2013 5:40 am
Posts: 72
Location: /home/sci4me
Okay, I think I understand now. My next question is about the addresses of devices... are the addresses hardcoded? If so, don't some devices overlap?


Top
 Profile  
Reply with quote  
PostPosted: Wed Oct 09, 2013 12:42 pm 
Offline

Joined: Sun Jul 28, 2013 12:59 am
Posts: 235
Typically, you arrange for devices to not overlap, although there are some schemes where devices are... "incompletely decoded" (meaning that the device "doesn't care" what some of the address lines are), but each have a preferred base address, and specific combinations of "don't care" address bits can be used to select multiple devices at once... Though you'd only want to do this for writing, not for reading. Bus contention isn't fun.


Top
 Profile  
Reply with quote  
PostPosted: Wed Oct 09, 2013 6:10 pm 
Online
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8546
Location: Southern California
sci4me wrote:
Okay, I think I understand now. My next question is about the addresses of devices... are the addresses hardcoded? If so, don't some devices overlap?

You determine their base addresses when you design your address-decoding circuit. The simplest address-decoding schemes will cause "mirrors," meaning that some devices may show up at more than one address; but that's ok, as long as there are addresses that let you talk to each device individually and there's no bus contention. Beginners tend to make it much more complex than it needs to be, leading to more work, parts, and board space, plus excessive propagation delays, limiting the speed the system can work at.

_________________
http://WilsonMinesCo.com/ lots of 6502 resources
The "second front page" is http://wilsonminesco.com/links.html .
What's an additional VIA among friends, anyhow?


Top
 Profile  
Reply with quote  
PostPosted: Wed Oct 09, 2013 8:57 pm 
Offline

Joined: Tue Oct 08, 2013 5:40 am
Posts: 72
Location: /home/sci4me
To be honest, I think it is a lot simpler than it seems. For some reason though, it seems confusing to me. IDK why.. but like I said, I think i got it now.


Top
 Profile  
Reply with quote  
PostPosted: Thu Oct 10, 2013 8:37 pm 
Offline

Joined: Tue Oct 08, 2013 5:40 am
Posts: 72
Location: /home/sci4me
Sorry to bump, but I just realized something. I was being a total idiot. Not really sure why it took me until just now as I am testing the first version of my emulator to realize that you can do something like this:

Code:
LDA #65
STA #8800


to (in my case atm) write an A to the terminal. I feel stupid.


Top
 Profile  
Reply with quote  
PostPosted: Fri Oct 11, 2013 2:07 am 
Offline
User avatar

Joined: Thu May 28, 2009 9:46 pm
Posts: 8513
Location: Midwestern USA
sci4me wrote:
Sorry to bump, but I just realized something. I was being a total idiot. Not really sure why it took me until just now as I am testing the first version of my emulator to realize that you can do something like this:

Code:
LDA #65
STA #8800


to (in my case atm) write an A to the terminal. I feel stupid.

STA #8800 is not a valid instruction. STA has no immediate mode operand.

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


Top
 Profile  
Reply with quote  
PostPosted: Fri Oct 11, 2013 2:38 am 
Online
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8546
Location: Southern California
IOW, if the port register is at address $8800, just remove the # and make it STA $8800.

_________________
http://WilsonMinesCo.com/ lots of 6502 resources
The "second front page" is http://wilsonminesco.com/links.html .
What's an additional VIA among friends, anyhow?


Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 22 posts ]  Go to page 1, 2  Next

All times are UTC


Who is online

Users browsing this forum: Google [Bot] and 72 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: