6502.org Forum  Projects  Code  Documents  Tools  Forum
It is currently Mon May 06, 2024 7:13 am

All times are UTC




Post new topic Reply to topic  [ 15 posts ] 
Author Message
 Post subject: Memory mapping vs. x86
PostPosted: Fri Sep 12, 2003 2:52 pm 
Offline

Joined: Thu Sep 11, 2003 5:47 am
Posts: 45
I just had a query about memory mapping (as it exists on x86 systems).

You insert a 128mb stick of RAM, which gets mapped into memory space from 0x00 to 0xwhatever (doesnt matter).
Now, you can read any of this RAM using the address/data lines.

How does the x86 go about remapping areas, for example a ROM image at 0xC0000.

What keeps the data lines latched to the ROM's data, rather than the RAM's data (which exists in the same address).
I could understand how this would work easily if the ROM was on-board (the motherboard), as it could just exclude that region from the RAM map.

But, ROM images can be located on add-in boards (I'm assuming ISA-style configuration for this example). The motherboard doesnt know if a ROM image is located on this board, so how does it map around it??

I just ask, as I think it opens possibilities for a 6502 system.

Cheers


Top
 Profile  
Reply with quote  
PostPosted: Fri Sep 12, 2003 9:20 pm 
Offline

Joined: Sat Jan 04, 2003 10:03 pm
Posts: 1706
Sentient wrote:
You insert a 128mb stick of RAM, which gets mapped into memory space from 0x00 to 0xwhatever (doesnt matter).
Now, you can read any of this RAM using the address/data lines.

How does the x86 go about remapping areas, for example a ROM image at 0xC0000.


Address decoding occurs in the motherboard chipsets. There is a collection of logic which checks the upper-most address lines against hardwired conditions. If these conditions hold true, the control signals to the RAM chips are not generated as usual; hence, the RAM doesn't interfere with other devices on the bus.

Quote:
What keeps the data lines latched to the ROM's data, rather than the RAM's data (which exists in the same address).


Nothing. The RAM itself does not respond because it's explicitly told not to respond by the address decoder circuitry. Hence, there is no contention between resources, as only one device is driving the bus at any given time.

Quote:
I could understand how this would work easily if the ROM was on-board (the motherboard), as it could just exclude that region from the RAM map.


At least one ROM is on-board. This ROM does not consume the entire address space dedicated to that purpose, however.

Quote:
But, ROM images can be located on add-in boards (I'm assuming ISA-style configuration for this example). The motherboard doesnt know if a ROM image is located on this board, so how does it map around it??


For ISA cards:

The motherboard assumes that ROMs exist on add-in cards. Hence, merely placing the ROMs on the add-in cards is sufficient to have it work. This is precisely why the early PCs had only 640K of memory capacity, instead of a full 1MB -- 128K was reserved expressly for video card framebuffers, and 256K of the 8088's address space is reserved for ROM BIOS and ROM expansions. This is hardwired into the motherboard and, unlike the Commodore 64/128, it cannot be banked out of the address space. But 640K ought to be enough for anyone, right?

This 384K mapping is still in existance today, and is a HUGE thorn in any OS developer's side. It sure would be convenient to have, say, a contiguous memory block from 0 to 0xWhatever (as you say), but in reality, there isn't. The memory is mapped as follows: 0x00000000-0x0009FFFF "Conventional RAM", 0x00100000-0xWhatever "extended RAM". The space between 0x000A0000-0x000FFFFF is reserved for a combination of I/O (video consumes 0x000A0000-0x000BFFFF), expansion ROMs (0x000C0000-0x000DFFFF), and ROM BIOS (0x000E0000-0x000FFFFF).

For PCI slots:

PCI devices work on a vaguely similar principle, but the address decoding circuitry is more evenly distributed between the I/O card and the motherboard. Hence, features enabling these expansion ROMs to be placed anywhere in CPU addressible memory is supported.

Each device is assigned a set of configuration register pairs, called "base" registers. When the device is reset, the device sets these base registers to all '1' bits. Unused bits are set to 0. This way, the computer can tell how much address space the device really needs. For example, a 128K ROM requires 17 address bits; hence, the lowest 17 bits are unused, and hardwired to zeros. Thus, upon board reset, the device has a default base address of $FFFE0000. Because the device has a cleared "device configured" bit somewhere, it knows to ignore any memory access to that address range. This is obviously important, since all uninitialized devices will have default base addresses somewhere up there anyway (e.g., 2GB RAM cards will have a default base address of $80000000, a 128K ROM will have $FFFE0000, an 16550 UART will probably have $FFFFFFFF0, etc).

When the BIOS, Windows, or other operating system enumerates the bus and sees an uninitialized device, it can tell by looking at the default base address how much space the device requires, allocates a chunk of memory space accordingly (note: because the lower 17 address bits of our hypothetical ROM are hardwired to 0s, it can only be given an address on an even 128K boundary!), and writes that address into the base address register (aka "bar" register). The "device initialized" flag is then set, and the device now knows how to decode the address bus for its specific device.

Obviously, to "allocate a chunk of memory space," the operating system or whatever requires some scratchpad RAM to work with -- this RAM is always present in the system, even at cold-boot time. Hence, motherboard-resident RAM, cold-boot ROM, and a minimum compliment of I/O registers are exempt from this I/O configuration system. Since the software must have these address (ranges) hardcoded into it to boot in the first place, it follows that it also knows enough to not overlap I/O device registers onto existing motherboard resources.

Quote:
I just ask, as I think it opens possibilities for a 6502 system.


The Commodore 64 had a full 64KB of RAM, and 24KB of ROM, plus 8K of I/O space. The Commodore 128 had a whole heck of a lot more (128K of RAM, expandable to 512KB in some models, plus over 100KB of ROM too!). It used memory banking hardware to switch between various "memory configurations," or "banks" as Commodore rather erroneously called them. This memory configuration hardware works by decoding addresses in various ways, based on how the MMU/bank registers were set.

Hope this helps out. If you need more concrete examples, I'm sure either I or someone else will be happy to supply hypothetical circuitry to help illustrate the principles.

BTW, I *do* believe in the viability of a fully auto-configuring expansion bus for both 6502 and 65816 systems, along the same lines as PCI (though not necessarily with the same electrical specifications as PCI). It would be highly beneficial, but probably not very worth the expense, and hence, rarely used. But, if someone were interested, I could spend some time to write up a draft specification for a standard 6502/65816 expansion bus.

--
Samuel A. Falvo II


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Sat Sep 13, 2003 3:52 am 
Offline

Joined: Thu Sep 11, 2003 5:47 am
Posts: 45
I won't deny it... I'm an information junkie.

The more I can get, the more I can figure out for myself - and hopefully, inspire others to ideas as well.

For a little more information on why I ask, I plan to design a simple (to start with) 6502 (or 65c02)-based personal computer.

I've done a little bit of thinking, and was just looking to bounce idea's off people who have also maybe considered this idea.

I would like the system to be modular in design (where possible), so I can retain backward compatibility with previous design, but still extend the system for more functionality.

With this idea in mind, a premilinary thought was to 'reserve' 4k of memory as an IO space, which is further divided into 'device pages' (perhaps 256 bytes, but I haven't considered this yet).

Each IO device that is installed could then have a 2 word (4 byte) ID in its IOSpace (at a fixed location, perhaps 0-1 and 2-3) that could be used as a device ID, along the same lines as PCI's Device ID.

This would mean that a device is not fixed to a specific location, and could be used as a means of detecting devices on an expansion bus.

Any comments/suggestions on these ideas, or any other ideas greatly appreciated.


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Sat Sep 13, 2003 4:52 am 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8432
Location: Southern California
Quote:
Any comments/suggestions on these ideas, or any other ideas greatly appreciated.

Instead of having these devices hanging on the processor's own buses (whether directly or through buffers), I would recommend implementing separate expansion buses primarily so you don't paint yourself into a corner as your future computers go faster.  Capacitive loading, transmission-line effects, slow EPROM speed, and so on will become limiting factors as you get past 5 or 10 MHz.  The common character LCD modules won't even work on the bus at much more than 1MHz, and I've used a really nice 16-pin all-in-one real-time clock IC that would only go about 250kHz.  If you put the extras out on an expansion bus through I/O ICs like the 65c22 VIA, those extras can be as big as you want and you can address them at their own speed without slowing the actual CPU down.  Address decoding on the processor's own buses becomes simpler, allowing faster glue-logic propagation delays, and you can have nearly the entire 64K filled with fast (12ns) SRAM.

My next system will have this kind of expansion, with selectable auto-increment for at least one of the 24- or 32-bit address buses.  The auto-increment option allows reading or writing a large table or file for example while each access automatically addresses the next byte with no extra processor overhead.  Page and block boundaries become 100% transparent.  There will also be multi-protocol serial buses that can handle I2C, SPI, Microwire, 1-wire, dumb shift registers, etc, since there are so many nice ICs on the market now with these interfaces and they're easier to build with because the serial interfaces don't require as many connections.  The lower pin counts also mean the ICs are smaller so you don't need as much board space.

_________________
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  
 Post subject:
PostPosted: Sat Sep 13, 2003 6:36 am 
Offline

Joined: Thu Sep 11, 2003 5:47 am
Posts: 45
Garth - would you mind explaining that idea in a little more detail. Im not sure I fully understand it (A block diagram would be most helpful).

Is any of my understanding close?

1) The expansion bus speed is independant of the CPU clock speed.
1a) If so, how is syncing handled?
2) Access is along the lines of CPU -> VIA -> Expansion Device(s)
3) Each device is associated with a VIA

I really could do with a simple block diagram of a system, because I'm sure I don't understand this as you meant it.
My interpretation of what your saying is that a series of VIA's are installed to provide access to each Expansion device. This seems less like a bus and more a generalized device access mechanism, which makes me think I misunderstand you.

Thanks for the idea, but a little more clarity would help


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Sat Sep 13, 2003 9:55 am 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8432
Location: Southern California
I already have parts of this info scattered around different areas of this forum, but I guess it's appropriate to write it all out here, so here goes.  If you find any dumb mistakes, it's because it's late.

Actually it'll all be done with just two VIAs, including extension buses, keyboard, display, data converters, large RAM & ROM for archives, files, large look-up tables for fast math, UARTs, etc., leaving various special-purpose pins available for those (like PB6 for a frequency counter and PB7 for clocking things at a constant rate determined by T1 plus of course the lowly but indispensable piezoelectric beeper), using other bit 6's and 7's for direct test-and-branching using the BIT instruction, keeping the bit 0's available for faster strobing using INC and DEC, the CA1's for interrupt inputs, and the CB1's and CB2's for synchronous serial for dumb shift registers.  The I2C, SPI, Microwire, SM Bus, and 1-Wire have to be bit-banged; but only 1-Wire and SMBus have any real timing requirements and they're much easier than bit-banging RS-232.  A 16MHz 65c02 or '816 is fast enough to bit-bang these interfaces about as fast as they'll go anyway.  I've prototyped a smaller version of the multiple serial bus and made it work on my current workbench computer, with an SPI UART, an I2C quad D/A converter, and a Microwire EEPROM on one little board.  The interface for it was only 9 wires, including +5V, +12V and -12V (for the MC145406 line driver and receiver for the UART), and ground.

A really extensive implementation of the multiple serial and parallel extension buses takes kind of a lot of 74HCxx support logic ICs, but one of the nice things about it is that they don't have to go on the SBC itself and you don't have to know ahead of time what or how much you'll want to put out there on the extension buses.  The various latches and registers and X-bus address decoding can go on the other boards that will go in the card cage when they get built, and they won't load the processor down any.  (This is why I did the VIA output current experiment a couple of days ago and posted the results here.  Most of what I wanted to make sure of was that they could drive fast edges into relatively high capacitive loads from a ton of CMOS inputs, WW IC sockets and wire, and open-collector lines with pull-ups like I2C uses for its bidirectional data line.)

I don't have the ideas all perfectly documented for someone else to make sense of them yet, but I can try to fax or snail-mail them to you if you want to try.  The computer will only have four VIAs, including the two to do the extension buses.  The other two will be mostly uncommitted and available for projects.  My current workbench computer (using a 65c802 at 5MHz) has three VIAs.  VIA1 is used for the keypad, LCD, beeper, real-time clock (using T1), printer, and a synchronous serial interface.  (I've put hundreds of bits of I/O on a single VIA's synchronous serial line on some automated test equipment I designed, built, and programmed at work a dozen years ago.)  The desire to get more on a VIA is what mushroomed into the idea of using two VIAs to interface to anything and everything while leaving two more uncommitted for projects.  Mike is working on getting some pages up on 6502.org with my pictures, schematics, explanations, etc..  Those should be done soon.  [Edit: See http://wilsonminesco.com/BenchCPU/ instead.]

Although beating around the bush with an attempt to broaden the view while giving a little more background, I think I more-or-less answered your questions #2 and #3.  (#2 is a "sort of yes" and #3 is "definitely no; it's not so limited")  As for question #1 and 1A: the VIAs' bit-changing speeds are determined by the software.  For a parallel bus, you go through the VIAs to manipulate the various lines to basically duplicate a 6502-type bus that's isolated and separate from the processor's own bus.  You can have more than one of these extension buses, and they can be as big as you want, and slow devices are accommodated by adding the necessary delays in software.

It might sound slow, but here's my justification.  Since every single instruction requires at least one byte and up to four (on a 65816), most of the computer's bus accesses are to program memory, variable space, stack space, etc..  This is true enough in assembly, but even more so in a higher-level language.  With the tons of I/O I have in mind for future workbench and industrial control, putting it on the processor's own bus would slow everything way down.  Even without the heavy loading, many of those things I want to interface are much too slow to go on a 20MHz 6502 bus even if they were the only thing there (along with the necessary memory).  For example, I have large look-up tables for 16-bit math functions in EPROM.  With these tables, most of which are 128KB each, you can, for example, look up the cosine or log of a 16-number in about 3 microseconds, and the answer will be as exact as 16 bits can get you.  (I'll send the .hex files to Mike to put on 6502.org too.  I suppose I should do another page showing how much can be done with fixed-point and scaled-integer math too.  It's common for people to think you have to have floating-point to do trig and other functions.  [Edit, 6/25/12: the tables, with supporting info, are posted here.])  With an '816, these tables certainly fit within its native address range—IF you latch and use the high address byte.  The '816 will run a little faster though if you don't; so even if there were ultra-fast EPROMs, you can run the computer a little faster using only the one 64K bank (bank 0) of its address space, which will be enough for my purposes.  I'll have fast SRAM in all but 256 bytes of it.

All in all, why not pay the small price of slowing the I/O down a little so that in general the computer can run much, much faster while having nearly limitless I/O?  The few things that really have to be fast can go on the other two mostly-uncommitted VIAs instead of through an extension bus, or connect them directly to the processor if they have a processor interface.  Those will be few however.  The extension buses can handle most of the hog-wild, ad-infinitum unforeseen peripheral additions, at least for my industrial uses.

The only thing I haven't totally worked out yet is how to handle some of the possible interrupt scenarios with as little additional logic and software overhead as necessary.  More on that another day.  I'm going to bed.

Garth

_________________
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  
 Post subject:
PostPosted: Sat Sep 13, 2003 10:13 am 
Offline

Joined: Thu Sep 11, 2003 5:47 am
Posts: 45
Thanks for the reply.

It definately sounds interesting, and makes a lot more sense now (I think) :)

I'd be interesting in seeing a diagramatic representation of your system, in particular addressing IO devices and transferring data.

From the sounds of it, you set the expansion buses data bit-by-bit - although I won't assume this to be the case.

As I said, sounds like an interesting idea and I look forward to hearing more on it.


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Sat Sep 13, 2003 5:27 pm 
Offline

Joined: Sat Jan 04, 2003 10:03 pm
Posts: 1706
Sentient wrote:
With this idea in mind, a premilinary thought was to 'reserve' 4k of memory as an IO space, which is further divided into 'device pages' (perhaps 256 bytes, but I haven't considered this yet).

Each IO device that is installed could then have a 2 word (4 byte) ID in its IOSpace (at a fixed location, perhaps 0-1 and 2-3) that could be used as a device ID, along the same lines as PCI's Device ID.


Apple has a system whereby the address of a device is dependent on which slot you insert it into. I do not recall the exact details, but the Apple II series came with 8 expansion slots. Each slot had the 6502's full address bus presented to it in case a memory expansion was desired; but for regular I/O devices, it had *pre-decoded* I/O selects which were decoded by the motherboard on behalf of the I/O device. Hence, you could locate the expansion card in any slot, without fear of changing addresses and the like. In slot 0, a device might occupy $E000-$E0FF, but in slot 6, it might appear at $E600-$E6FF.

NOTE: ROMs containing code (e.g., supplying the device's driver on the card itself to attain 100% plug-n-play) must be prepared to handle execution from any address. The 6502 has a pretty static execution environment -- except for branches, all PC-affecting instructions do so with absolute or absolute indirect addresses. 99% of all zero-page and non-ZP memory accesses are done with absolute addresses. X-register-indexed is insufficient to make a truely location-independent piece of software, due to it being relatively slow and the X register being only 8-bits wide.

To work around these problems, you could take the route that the Atari ST uses to boot its own OS up: a ROM-disk. The basic idea is to store your software in ROM in a relocatable execution format, such as O65, in ROM. Recognition of the device requires "loading" the ROM-resident software just as if it were disk or tape-resident. The advantages are as follows:

* The software can be relocated anywhere in memory at boot-time.

* The software, once loaded, can be unloaded and its memory reclaimed for other uses if and when necessary. For example, as I'm typing this content, my floppy disk isn't doing a single thing. In fact, I rarely ever use the floppy disk. Even the harddrive itself can be said to not be doing anything useful (certainly it wouldn't in a 6502 environment, where memory protection and virtual memory doesn't exist). Hence, why take up space with a floppy or IDE device driver when it's not being actively used? I could be devoting more of my limited (<64K) RAM to the content being edited. Then, when I need to load or save some data, the OS can at that time reload the IDE/floppy device driver on an as-needed basis. (Of course, it needs RAM to do that with; this is an advanced application of the system; for a simple system, you wouldn't typically do this. I'm just saying this is possible without any complicated virtual memory hardware, nor with a sophisticated I/O bus design.)

* The software isn't limited to running in a 6502 environment -- if, at a later time, you want to upgrade to a 65816-based solution, the software can still be loaded and used anywhere in the 65816's address space, including between 64K and 16MB regions.

* Heck, it's not even limited to the 65-architecture at all. If you decide to make a homebrew PC based around a simple RISC or 68K-series processor, you can easily make a 6502-to-native-machine-language translator. Yes, there's a run-time performance hit. But there's no arguing -- it'll still work, and that's pretty much the point, isn't it?

* It is CPU-speed independent -- RAM is always as fast as the CPU, while most of the cheaper ROM chips can handle at most 4 to 8MHz depending on bus loading.

* It's simpler to hook into the system: a serial ROM typically has only 8 pins to the chip (two of which are power, two of which actually carry the data/clocking required to access the data, and the remaining four are typically address select pins if you want greater storage capacity; most serial ROMs can allow up to 8 devices to be placed on its I2C serial bus, giving the illusion of storage up to 512KB!!), and can hold up to 64K in a single unit. Hence, it can not only store the driver proper, but any default "support files" it needs too.

* If serial ROMs were anticipated by the I/O bus specification, it could support a special out-of-band serial interface which could be an intrinsic part of the motherboard registers, which can greatly simplify the interface to such hardware.

* Because the ROMs hold a filesystem image, instead of raw executable content, a filesystem standard can be created that allows device capability inquiries to be made: vendor IDs, minimum requirement lists, whether or not it's a boot-capable device, and if so, what it's default priority is in the system, etc.

* The filesystem image need not be even as complex as FAT; since it exists in ROM, there is no need for a block allocation table, etc. Directories can be implemented using very simple data structures, like a linked list, without a performance penalty, etc. Support for subdirectories are not typically needed, but it can be supported easily using only a single flag-bit in each directory entry node.

2) Devices have a limited register space for I/O registers. This is not usually a concern, but it can be if you are adding a device which requires a large amount of memory for its own use (e.g., a video card often requires a video frame buffer). For example, you're expecting to allocate 4KB to devices. If you assume a maximum of 16 slots, that makes for 256 bytes of register space per slot. 256 bytes is woefully inadequate to address a video frame buffer, even for a plain, monochrome, text-mode display. One solution to this is to use a video system whereby you access frame buffer memory through a set of ports, such as the Commodore 128's VDC chip, or the TI-99/4A's VDP. To get an idea of what I mean, you can reference the VDP's documentation online. Here's a good link that I found while googling for "9918A datasheet": http://www.msxnet.org/tech/tms9918a.txt If the peripheral you're looking to interface doesn't support memory-access-through-registers, it's usually not too hard to implement it yourself.

Other than these two "gotchas," as I can't even call them disadvantages really, a slot-dependent-base-address technique is not only exceptionally powerful, it's simple, cheap, and for a simple bus interface like the 65816, damn fast. This is the type of bus interface I was going to use for my own devices in my own homebrew environment. My system, inspired by Garth's own design concepts, was not going to have a BIOS ROM or such, except purely for the purposes of cold-booting. Instead, it was going to draw its system software from a serial EEPROM, probably a 24C512 series chip. After loading the system software into RAM, it would write-protect that region of RAM, so that it emulated ROM as far as the rest of the software was concerned.

I cannot fathom why the PC bus interface didn't follow this approach. ISA, arguably the most useful bus interface second only to PCI, could easily have handled full plug-n-play and still retained its hacker-friendly interface. The only disadvantage to ISA would be its 8MHz speed, which could easily have been addressed by revising NOT the software specifications, but the hardware specifications alone. As it is, PCI was a world unto itself, specifying both software and hardware requirements. Fortunately, PCI got a different approach towards autoconfiguration which is equally viable and, while not hacker friendly, it has a long-life (evidence: AGP is actually a point-to-point version of the PCI bus, operating at MUCH higher speeds; it is 100% software compatible with PCI, and even appears as a PCI bus when enumerating the system hardware through the PCI configuration register space).


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Sat Sep 13, 2003 7:36 pm 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8432
Location: Southern California
Quote:
I plan to design a simple (to start with) 6502 (or 65c02)-based personal computer. . .

I would like the system to be modular in design (where possible), so I can retain backward compatibility with previous design, but still extend the system for more functionality.

There's a lot in the archives I think you would be interested in.  To see one particular thread that comes to mind, see the topic "Survey: A 65C02-based PC".  There's a lot there about hardware expansion, modular design, operating systems, BIOSes, etc..

It's easy to ask old questions and even to get carried away writing things again that we've already written before; but there's quite a wealth of idea exchanges to be had first for the reading.  Take a few evenings and go through the stuff on this forum, the Delphi forum, and the Yahoo forum.  You'll find links to these other two by going to 6502.org front page and clicking on "Discussion groups".

You'll find kind of a recurring theme in that someone will say "If we make a 6502 computer everyone will like, what does everyone want?"  The discussion seems to get out of hand in that the ideas get too grandiose to pull off for a bunch of hobbyists with outside full-time jobs, so this grand computer never gets made.  There are no heated disagreements, but neither is there any strong agreement on a large spec.  Then someone like Daryl comes along and says, "I'm going to make myself a little computer board.  Anybody else want one?" and he got 20 or 30 orders.  He took refinement suggestions from us, but did not let anyone pull him away from the goal, which was to actually get it made in a reasonable amount of time.  Several of those are running on workbenches now.

I'm definitely not downplaying the value of the constant discussion on what to incorporate in our computer designs and how to do it—I always enjoy them and I think they've been helpful.  And while my own progress on the "next one" has been years in coming, I continue to get a lot of good use out of the one I'm using now, and I don't mind sharing what I've learned regarding practical applications with it and the three or four previous ones.

_________________
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  
 Post subject:
PostPosted: Sun Sep 14, 2003 6:43 am 
Offline

Joined: Fri Jun 27, 2003 8:12 am
Posts: 618
Location: Meadowbrook
Garth, I am trying something interesting with my /IRQ setup. The first time I use it is to test the /IRQ. Of interest in a trick I learned from another programmer,. I have a zero page memory location called IrqSource. This byte has the /irq loaded from the 6522 or elsewhere to tell what irq actually happened. In effect, it mirrors the IFR register. For starters, since I am using only one 6522 on my SBC, if its a $00, then the irq is external. The IFR gets reflected. I do a small trick to test out the IRQ by loading in a funky number into IrqSource, then checking to see if the irq routine gets serviced. Let me show you the code all around.

Also, I am having a second byte I call IrqMode. Any other routine or device can plant into this location to tell the /IRQ routine what the heck to do with things, so you can use that as a flag trick. Anyhoo, without further ado...

; * * * Equates Table * * *

; Zero page, can be changed over at any time.

CountLSB = $00 ; Temp variables for counters
CountMSB = $01
TempLSB = $02 ; Temp variable
TempMSB = $03
IrqSource = $04 ; Location where IRQ is stored. Read here to find out what happed.
IrqMode = $05 ; Location that IRQ looks at for what to do.

then later in the code is the testing routine to test irq. IT will NOT test the 6522, it only really tells that the irq line is stuck.

; Thanks to Great Plains Electronics to allow the 6532 testing routines adapted my own way :)

; 4th flash: 6522 IRQ test ok.
; 5th flash: 6522 bit walk test ok.
; 6th flash: 6522 timer test ok.

ViaTest LDA #$00 ; IrqSource tells where the irq is from.
STA IrqSource ; $00 is external irq, other #s are Via_IFR
LDY #$0F ; Clear out all 16 VIA registers
ViaTest1 STA Via_ORB_IRB,Y
INY
BNE ViaTest1
LDA #$5A ; bogus IRQ number, cannot happen in real life.
STA IrqSource ; Location which tells what IRQ was done. If $00, then it was an external.
CLI ; Ok, NOW let's begin checking out the IRQ set.
NOP ; If it goes into IRQ at this point, its a bad irq and is considered a bye bye.
NOP ; It could be external or a bad 6522; not sure.
NOP
NOP
SEI ; Ok, I passed the irq test, now shut down irq checking.
JSR FlashLedOnce ; Ok, the IRQ is tested good.


and here is the irq routine...

IRQ PHA ; First, let us save the 6502 registers. A first.
TXA
PHA ; save X
TYA
PHA ; save Y
SEI ; I dont want to get interrupts while being interrupted. Cmon...
LDA IrqSource
CMP #$5A ; Test for the bogus
BEQ IrqTestFail ; The IRQ is held low and failed!
LDA IrqSource
CMP #$00 ; Check for an external interrupt
BEQ ProcessExternalInterrupt
LDA IrqSource
CMP Via_IFR ; Check the interrupt flag register in the 6522 to see if it came from there.
BEQ ProcessViaInterrupt ; Valid 6522 interrupt, work on it.
JMP RestoreRegisters

IrqTestFail
JMP IDidBad ; IRQ stuck, let us die...

ProcessExternalInterrupt
NOP ; To take care of external interrupts. Depends on what is to be written..
JMP RestoreRegisters


ProcessViaInterrupt
LDA Via_IFR ; Get the interrupt byte
ASL
BMI Timer1Interrupt
ASL
BMI Timer2Interrupt
ASL
BMI CB1Interrupt
ASL
BMI CB2Interrupt
ASL
BMI ShiftRegInterrupt
ASL
BMI CA1Interrupt
ASL
BMI CA2Interrupt
JMP RestoreRegisters


Timer1Interrupt ; The individual interrupt processings, nothing here for now until later on...
NOP
JMP RestoreRegisters

Timer2Interrupt
NOP
JMP RestoreRegisters

CB1Interrupt
NOP
JMP RestoreRegisters

CB2Interrupt
NOP
JMP RestoreRegisters

ShiftRegInterrupt
NOP
JMP RestoreRegisters

CA1Interrupt
NOP
JMP RestoreRegisters

CA2Interrupt
NOP


RestoreRegisters
PLA ; restore, pull that Y back
TAY
PLA ; pull that x back
TAX
PLA ; finally, pull a back
CLI ; Re-enable the interrupts once again.
RTI ; done, go back to work.


This way, I have it expandable for what UI need for /irq servicing. I may never use any of it, but its ready, just in case :)

PS: the irq test is already working, if I short out the irq line to ground, it wont give that 4th flash, so am happy with it :) the bit walk I am having trouble, so its debug, debug, debug madness :)

_________________
"My biggest dream in life? Building black plywood Habitrails"


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Sun Sep 14, 2003 7:21 am 
Offline

Joined: Sat Jan 04, 2003 10:03 pm
Posts: 1706
I've wrapped your code in code blocks to better distinguish your code from non-code.

Nightmaretony wrote:
Code:
      LDY #$0F   ; Clear out all 16 VIA registers
ViaTest1   STA Via_ORB_IRB,Y
      INY
      BNE ViaTest1


What, exactly, is this code supposed to do? Should the INY not be a DEY? Like this:

Code:
  ldy #$0F
ViaTest1:
  sta Via_ORB_IRB,y
  dey
  bpl ViaTest1


The reason I ask is because after the first loop iteration, the Y register will contain $10, which is beyond the last register of the chip. Plus, it loops for almost 240 bytes worth of memory, since BNE sees a true condition for everything except when Y wraps to 0.

Quote:
Code:
ViaTest    LDA #$00   ; IrqSource tells where the irq is from.
      STA IrqSource   ; $00 is external irq, other #s are Via_IFR
   . . .
      LDA #$5A    ; bogus IRQ number, cannot happen in real life.
      STA IrqSource   ; Location which tells what IRQ was done. If $00, then it was an external.


Why are you clearing IrqSource to start with, then changing it afterwards, with no intervening IRQ occuring in between? I'm just curious, because it is confusing to me.

Quote:
Code:
IRQ   PHA         ; First, let us save the 6502 registers. A first.
   TXA
   PHA          ; save X
   TYA
   PHA          ; save Y
   SEI         ; I dont want to get interrupts while being interrupted. Cmon...


I would consider putting the SEI instruction before anything else; otherwise, an interrupt can (and if the IRQ line is stuck low, will) occur between the last vector pull and the SEI instruction. That is, I'd write the following:

Code:
IRQ:
  sei
  pha
  txa
  pha
  tya
  pha
  . . .


Quote:
Code:
   LDA  IrqSource
   CMP #$5A      ; Test for the bogus
   BEQ IrqTestFail      ; The IRQ is held low and failed!   
   LDA IrqSource


There is no need for the second LDA -- it already contains the same value from the first load. Ditto for the other comparison locations in your code.

Quote:
Code:
ProcessViaInterrupt
   LDA Via_IFR   ; Get the interrupt byte
   ASL
   BMI Timer1Interrupt
   ASL
   BMI Timer2Interrupt
   ASL
   BMI CB1Interrupt
   ASL
   BMI CB2Interrupt
   ASL
   BMI ShiftRegInterrupt
   ASL
   BMI CA1Interrupt
   ASL
   BMI CA2Interrupt
   JMP RestoreRegisters


As written, this software will read the IFR, then handle precisely one interrupt source. It is possible for multiple interrupt sources to occur in a single IRQ, however. IIRC, reading the VIA's IFR will clear it, which will cause a potential loss of interrupts.

I'd write it this way instead:

Code:
ProcessViaInterrupt:
  lda VIA_IFR
  bpl pvi1
  jsr Timer1Interrupt
pvi1:
  asl
  bpl pvi2
  jsr Timer2Interrupt
pvi2:
  asl
  bpl pvi3
  jsr CB1Interrupt
pvi3:
  asl
  bpl pvi4
  jsr CB2Interrupt
pvi4:
  asl
  bpl pvi5
  jsr ShiftRegisterInterrupt
pvi5:
  asl
  bpl pvi6
  jsr CA1Interrupt
pvi6:
  asl
  bpl pvi7
  jsr CA2Interrupt
pvi7:
  jmp RestoreRegisters


In doing things this way, we keep the entire IFR contents in the A register for processing -- we shift the bits out one by one, and we test them via the sign bit. Each subroutine is responsible for saving and restoring the A register, of course. This way, we don't lose the precious information that the IFR delivers to us only once.

If you really want the ultimate in expandability for your software, you could even write the following:

Code:
Timer1Interrupt:
  jmp (Timer1Vector)

Timer2Interrupt:
  jmp (Timer2Vector)

. . .

StandardIgnore:
  rts


The idea is that you have a set of vectors set up (e.g., Timer1Vector = $0200, Timer2Vector = $0202, etc). By default, they would point to StandardIgnore, so that should a spurious interrupt occur, no damage will occur. Otherwise, to "hook" the handler, you just load your routine's pointer into the appropriate vector. Chaining handlers is possible by first preserving the vector contents in a temporary variable somewhere in memory, and indirect jumping to it (e.g., as with Commodore 64's IRQ handlers and MS-DOS TSRs).


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Sun Sep 14, 2003 7:49 am 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8432
Location: Southern California
I'm not sure I totally understand the goal.  There are several things that can help you shorten the code though, especially if you have the CMOS 6502.

If you connect the same reset line as used for the uP to the VIA's RST input, the registers will start out cleared already.  I've never had to reset the VIA in software.

As for all the NOPs— If you just have CLI followed immediately by SEI, there's time for an interrupt in between.  BTW, SEI on the 6502 always works.  It's not like on the PIC microcontrollers.  On the PICs, if an interrupt hits during the instruction to clear the the GIE bit, the ISR is still executed and then the RETURN instruction again restores the GIE bit.  So on the PICs, you have to test with a loop and send it back if interrupts did not get disabled.  Even if you don't have to branch back and repeat the loop, the PIC takes 12 clocks to do what the 6502 does reliably in 2.

The IRQ sequence already sets the interrupt-disable flag, so you don't have to start with SEI, and you definitely don't want the last instruction before RTI to be CLI.  Just omit those.  The CLI before RTI may be the source of the problem you mention at the end.  If the cause of the interrupt has not been taken care of so the IRQ\ line is still low, the ISR will be entered again immediately following the CLI, before the RTI has gotten the return address and the status byte off the stack.  After a bunch of loops of this, the stack will be overrun.

Your sequence:
TXA
PHA ; save X
TYA
PHA ; save Y
can be replaced with:
PHX
PHY
if you have the 65c02.  At the end, use
PLY
PLX

In your sequence:
LDA IrqSource
CMP #$00 ; Check for an external interrupt
the CMP #00 is automatically included in the LDA IrqSource.  There's no need to repeat it.

In your
JMP RestoreRegisters,
the 65c02 can use the BRA (branch relative always) instruction if you won't be branching more than 127 bytes away.  It still takes 3 clocks in most cases, but saves a program byte.  With the NMOS 6502 (with no BRA instruction), you may still know the state of one of the flags so you can use a branch instruction whose condition you know will always be met.

As for determining if it was the VIA that interrupted, just do
BIT  VIA_IFR
followed by BPL or BMI.  You don't even need to affect A, X, or Y.  In fact, if you had the VIA in ZP (somewhat unlikely), you could do the entire test and branch with the BBS7 or BBR7 instruction of the 65c02.  Then a single instruction tells it what address to check, which bit at that address, what condition to branch on, how far to branch, and in which direction.

As for testing all the interrupt bits:  I find that it's pretty rare that you'll have more than about three interrupts enabled at once in the entire computer, let alone in one chip.  And since all applications won't necessarily respond to a given interrupt source the same way, the ISR may as well test only the enabled interrupts.  One of the 6502's greatest strengths is the fact that its interrupt response is faster than any other 8-bitter.  In fact, with off-the-shelf parts, it is entirely possible to have an interrupt, including overhead, take only one microsecond before the regular program picks back up where it left off.  Having efficient ISRs will help take advantage of the 6502's interrupt efficiency.

My Forth system includes an efficient way to install, prioritize, and delete ISRs written both in Forth and in assembly.  This allows setting up a new interrupt without re-writing or modifying the ISRs that were already in effect.  The same goes for deleting an ISR when you're done with it.  ISRs are assigned a priority at installation time, and the installer software puts them in the proper order so that if two or more interrupts hit at the same time, the higher-priority one won't be kept waiting by the lower-priority one.

I have a primer on interrupts I'll have Mike post on 6502.org after he's done with the other stuff he's putting up for me.  [Edit: It's up, here.]  I wrote it because some people on another forum seemed to turn glassy-eyed when we'd talk about interrupts.  I remember one student even grossly misunderstood the roles of hardware versus software interrupts.

_________________
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  
 Post subject:
PostPosted: Sun Sep 14, 2003 7:31 pm 
Offline

Joined: Fri Jun 27, 2003 8:12 am
Posts: 618
Location: Meadowbrook
Whew! Thanks there on the hints. The firts bit of code was to merely clear the 6522 registers. The reset IS connected, but I consider it a good programming practice to double check the clearing :)

I will throw oyur suggestions into my code, thanks mnucho. Also, am using a regular 6502 and 6522 in there, moight upgrade if the price is right. (at work, I think there are a couple of thousand 6502's hanging around :)

Time to print and change and learn things...


The 4 nops inthe row I am using for processor speed, thats what I seen in that one routine and it seems to like to work, so am not going to mess with that portion of things. The irq processing can definitely get the ugprades you mentioned.

I havent had a recursive loop stack problem uyou mentioned, but good to catch it now before later on.

Thanks mucho there....

_________________
"My biggest dream in life? Building black plywood Habitrails"


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Sun Sep 14, 2003 9:05 pm 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8432
Location: Southern California
Quote:
Also, am using a regular 6502 and 6522 in there, moight upgrade if the price is right. (at work, I think there are a couple of thousand 6502's hanging around

You can get a WDC 65c02 from Mike for a few bucks.  (I don't know the exact prices but I think they're on this website.  I do know it's well worth it for you to upgrade.)  [Edit: See our sticky topic, "65xx parts sources, genuine and fake".]  OTOH, don't let those 6502's at work slip away.  You know how it is when they get some manager in there who's a real go-getter and when he gets an idea it's got to be done right now.  He may say "Those worthless dinosaurs are just taking up room.  Throw them out!" and they'll be gone before you even knew anyone brought up the subject.  Even the old NMOS ones will always have some value to people who have purposely taken advantage of the strange behaviors of the unofficial op codes (which I find interesting but only marginally useful).  According to Samuel Falvo, Berkeley Softworks even used these op codes when they wrote the GEOS GUI for the C64 before the 65c02 was out.

Quote:
I havent had a recursive loop stack problem uyou mentioned, but good to catch it now before later on.

I can allow for the possibility that you did not include all the code; but as it is, you don't show anything that clears the interrupt condition in the VIA, meaning that the processor will repeat the interrupt sequence again as soon as it gets to the CLI before the RTI, which definitely means you will overrun the stack on very short order.  There are times that it will be appropriate to do a CLI to allow other interrupts during the servicing of the first one, but you must first take steps to keep an individual interrupt source from unwittingly asking for service more than once for any given interrupt event.

Quote:
The firts bit of code was to merely clear the 6522 registers. The reset IS connected, but I consider it a good programming practice to double check the clearing

If the VIA's state were totally unknown at reset, it could cause a lot of trouble, potentially even damaging equipment.  For example, a VIA was used to control the print head on the AIM65 computer, and the manual specifically said the average user should not try to write or alter printer-driver code because accidentally leaving the power turned on to any given dot too long would burn it, permanently damaging the print head.  The designers foresaw this danger and made sure the reset truly gave you a known set of conditions.  Instead of expressly clearing the VIA in software after power-up, it is enough for each routine that sets up part of the VIA for a particular purpose to do exactly that, setting or clearing the appropriate bits in the ACR, PCR, DDRA & DDRB, etc..

Garth

_________________
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  
 Post subject:
PostPosted: Mon Sep 15, 2003 3:46 am 
Offline

Joined: Fri Jun 27, 2003 8:12 am
Posts: 618
Location: Meadowbrook
At work, I design and repair classic arcade games. Almost all Atari games use 6502, for example. The board itself I am programming is an SBC type which is my all in one solution I have plans using it for pinball machines and other games and haunted house robotics. One or two instant response haunted house applications, the self test will be skipped entirely.

The board is similar but a little simple than Darryl's SBC. I use a 6522 and a 27256 eprom and ram. 7 chips total. Works out nicely. The flashLedOnce is a diagnostic routine which tells me that a power up self test actually worked.

will jump the modifications in this week. Anyhoo, thanks again. will send you the rest of the code if you like.

_________________
"My biggest dream in life? Building black plywood Habitrails"


Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 15 posts ] 

All times are UTC


Who is online

Users browsing this forum: No registered users and 9 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: