6502.org Forum  Projects  Code  Documents  Tools  Forum
It is currently Mon Nov 11, 2024 2:51 am

All times are UTC




Post new topic Reply to topic  [ 5 posts ] 
Author Message
PostPosted: Tue Feb 16, 2016 6:56 pm 
Offline
User avatar

Joined: Wed Aug 17, 2005 12:07 am
Posts: 1250
Location: Soddy-Daisy, TN USA
I have a SBC I assembled (it's the L-Star) and I'm having a blast with this thing. So I'm building an expansion card for it and I need to be able to halt the CPU (65C02) to handle some external I/O.

Unfortunately, BE is not available without modifying the board (which I do not want to do).
The only pins I have access to via my expansion board are Addr/Data, IRQ, NMI, SOB, SYNC, R/W and PHI2.

The schematics for the L-Star board I have are here: https://github.com/jacgoudsmit/L-Star/releases/download/0.1/L-StarPlus.Schematic.pdf

My plan was to have the micro-controller halt the CPU by holding PHI2 low (the mcu generates the 1MHz clock). Then, while the CPU is stopped, use the address lines to signal another micro-controller with some data (located on my expansion board). Once the expansion mcu is finished, it would then use the address pins to signal the host micro-controller that it was done.

So the two mcu's would be turning address pins on/off while they communicate.

Q: Is this going to cause any problems with the 65C02?

Q: Should I wait until PHI2 is low before I allow the clock to be stopped?


In case you're wondering, the mcu on the expansion card will monitor the RAMEN pin from the host. When it is HIGH, the host RAM chip will be disabled...meaning we're in "I/O" mode. The host will monitor a specific address and when it's captured, disable the RAM.

Thanks!

_________________
Cat; the other white meat.


Top
 Profile  
Reply with quote  
PostPosted: Wed Feb 17, 2016 2:48 am 
Offline
User avatar

Joined: Thu Jun 23, 2011 2:12 am
Posts: 229
Location: Rancho Cucamonga, California
Hi cbmeeks,

cbmeeks wrote:
My plan was to have the micro-controller halt the CPU by holding PHI2 low (the mcu generates the 1MHz clock). Then, while the CPU is stopped, use the address lines to signal another micro-controller with some data (located on my expansion board). Once the expansion mcu is finished, it would then use the address pins to signal the host micro-controller that it was done.


Can you elaborate on what exactly you want to do? It sounds like you're trying to make this unnecessarily complicated by using the address bus for signaling. It's probably much easier to just let the 65C02 run a program that e.g. reads a byte from a location that your MCU catches, and then writes that byte into memory. You could use the SOB pin and the BVS instruction on the 65C02 to let it know that it received the last input from the I/O device (the MCU).

Quote:
So the two mcu's would be turning address pins on/off while they communicate.


The 65C02 controls the address bus at all time, even when you hold the clock low, so you can't use the address pins for signaling (unless you use BE to take the 65C02 off the address line). However, during Phi1 (i.e. whenever the clock is low), the 65C02 doesn't use the data bus, so you can stop the 65C02 and use the data bus to communicate between two microcontrollers. You can make this wicked-fast by implementing a smart protocol that uses all 8 data bit lines at the same time (but half-duplex, so while one MCU writes, the other one reads). Depending on which MCU you use, you could probably transfer several megabytes per second this way, but it's probably difficult to implement. Alternatively, you could simply use a known protocol such as SPI or I2C or RS-232 to communicate data between the MCUs over the data bus while the 65C02 is "parked" with CLK0 low.

Quote:
Q: Is this going to cause any problems with the 65C02?


When the 65C02 is stopped in Phi1 (CLK0 low), it really doesn't care what happens on the data bus. But you can't manipulate the address bus for communication because it's always under control of the 65C02. You will damage the 65C02 if you try to override the address bus.

Even if you would use BE to take the address bus offline (while the clock is stopped, obviously), it would still be a dangerous thing to use the address bus as signals. There could be many other devices on the address bus that will interpret it as such: an address. If you twiddle with the address bus pins and accidentally form an address that one of those devices listens to, that device could behave in some way that you may not be able to predict.

Bottom line: manipulating the address lines can't be done without using BE, and even with BE it's really a bad idea to just use the address bus pins as signal lines. Don't do it.

Quote:
Q: Should I wait until PHI2 is low before I allow the clock to be stopped?


If you want to have the data bus available to you, it makes most sense to stop the clock when it's in low state, yes.

The difficult part of stopping and restarting the clock is that you have to make sure that the last pulse (before stopping) and the first pulse (after restarting) are long enough. The current L-Star firmware doesn't bother to do this correctly, because it only starts the clock once: when the system starts up and the 65C02 needs to go through a reset cycle anyway. One day I will rewrite the clock cog so it inspects the current state of the timer to make sure that it doesn't generate any clock pulses that are too short, before it stops the clock. Also, the current clock module can only stop the clock in Phi2 because of how the Propeller works (the actual pin output is ORed between the OUTA value and the timer output so the easiest way to stop the clock is to put a 1 in the bit in OUTA).

Quote:
In case you're wondering, the mcu on the expansion card will monitor the RAMEN pin from the host.


The RAMEN pin is not on the expansion port. It wouldn't do you any good to monitor it anyway, because the code that controls it (which doesn't exist yet) will monitor the clock pin. During normal operation, the RAM chip will only be enabled during Phi2. So if you stop the clock, RAMEN will not become active and the RAM chip will stay off the data bus.

Quote:
... the host RAM chip will be disabled...meaning we're in "I/O" mode. The host will monitor a specific address and when it's captured, disable the RAM.


The RAM chip control code (which, again, I still have to write), will make it possible to do DMA (which I think is what you're trying to do) by making it possible to enable the RAM chip for certain addresses during Phi1, and feeding STA <absolute address> instructions to the 65C02. Then all your MCU would need to do is put the data on the data bus at the right time, and wait until the clock goes high. This is all a bit "up in the air" but it will work when it's done, and it'll be documented in the source code. The module will probably be called RAMcontrol.spin, so if you see it on Github, that's probably the module you want to read.

===Jac


Top
 Profile  
Reply with quote  
PostPosted: Wed Feb 17, 2016 2:55 pm 
Offline
User avatar

Joined: Wed Aug 17, 2005 12:07 am
Posts: 1250
Location: Soddy-Daisy, TN USA
jac_goudsmit wrote:
Hi cbmeeks,
Can you elaborate on what exactly you want to do? It sounds like you're trying to make this unnecessarily complicated by using the address bus for signaling. It's probably much easier to just let the 65C02 run a program that e.g. reads a byte from a location that your MCU catches, and then writes that byte into memory. You could use the SOB pin and the BVS instruction on the 65C02 to let it know that it received the last input from the I/O device (the MCU).


Sure, I'd be glad to. I'm still toying with ideas for an expansion card for the L-Star. My first instincts are always audio/video. So I was thinking of having an external Propeller that drives them both using some video drivers I have. The external Propeller would act as a dedicated video buffer (sorta like the TMS9918) of 8-16K of video RAM. It would draw an image from its internal RAM. The host computer (L-Star) would send commands to the card for changing video RAM, drawing chars, etc. I realize this is usually slower than direct DMA. The TMS9918 suffered the same way. But there are things you can do to get around it. Plus, if the video generator had internal commands to do complex work (sprites, etc.) then it would be just a few commands from the host to do some complicated things. Like a display list for Atari or copper list for Amiga. So I didn't want the 65C02 sending a constant stream of data for anything. It needs to "set and forget". This applies to audio too. My idea was to implement an SD card on the expansion board. This card could contain a list of VGM files (video game music). The host sends a few simple commands to the expansion ("play castlevania.vgm") and the expansion Propeller would stream the music from SD while the host can do other things. Imagine playing around with Krusader while chiptunes are playing. All on a nice, color NTSC or VGA screen.

So the problem I was trying to solve was:

1) Efficiently communicate with expansion Propeller by sending commands (like the 65C22)

2) Re-route video through the expansion board.

3) No modifications to the L-Star other than using the built-in jumpers. Preferably no jumper wires either. I know the L-Star is a hobbyist computer. But the fun for me is treating it like it is an Apple I rolling off the assembly line and I'm building expansion cards for it. :-D

I didn't mention that I would disable the L-Star video and take that pin to control RAMEN. Because video would be hosted by the expansion board. Once I can communicate with the expansion board Propeller, the sky's the limit.


jac_goudsmit wrote:
The 65C02 controls the address bus at all time, even when you hold the clock low, so you can't use the address pins for signaling (unless you use BE to take the 65C02 off the address line). However, during Phi1 (i.e. whenever the clock is low), the 65C02 doesn't use the data bus, so you can stop the 65C02 and use the data bus to communicate between two microcontrollers. You can make this wicked-fast by implementing a smart protocol that uses all 8 data bit lines at the same time (but half-duplex, so while one MCU writes, the other one reads). Depending on which MCU you use, you could probably transfer several megabytes per second this way, but it's probably difficult to implement. Alternatively, you could simply use a known protocol such as SPI or I2C or RS-232 to communicate data between the MCUs over the data bus while the 65C02 is "parked" with CLK0 low.


Ah, that's some good advice. I didn't realize that about the address bus. I knew the BE pin was the "proper" way to do it. But, I don't see that the BE pin is exposed easily with the L-Star.

Utilizing the data bus, I could probably come up with a protocol that allowed the host/expansion to send data back/forth that way.

jac_goudsmit wrote:
When the 65C02 is stopped in Phi1 (CLK0 low), it really doesn't care what happens on the data bus. But you can't manipulate the address bus for communication because it's always under control of the 65C02. You will damage the 65C02 if you try to override the address bus.


I am curious on how the Apple II did it, however. I know the video controller used the RAM during the PHI1 (LOW). But wouldn't it have to change the address bus to get the video data?


jac_goudsmit wrote:
If you want to have the data bus available to you, it makes most sense to stop the clock when it's in low state, yes.

The difficult part of stopping and restarting the clock is that you have to make sure that the last pulse (before stopping) and the first pulse (after restarting) are long enough.


Ah, good point.

But...assuming 1 MHz, that is 500ns per half-cycle. So if I were 20ns into the LOW half-cycle or 400ns into the HIGH half-cycle, could I not stop it indefinitely if I wanted to? I mean, how would the CPU even know? I've always thought of the clock as time and by stopping it, the CPU has no notion of time anymore.

Oh, wait...I think I see what you're saying....

So if I was 20ns into the LOW half-cycle, I would need to make sure when I started the clock back up, I would run another 480ns at LOW right?

Like:

Code:
LOW --> 20ns    * STOP CLOCK...DO LOTS OF WORK *   Finish LOW with 480ns    HIGH....
___________                                                              ____________________|----------


Is that what you mean?


jac_goudsmit wrote:
The current L-Star firmware doesn't bother to do this correctly, .....


I would more than likely re-write the clock code.


jac_goudsmit wrote:
The RAMEN pin is not on the expansion port. It wouldn't do you any good to monitor it anyway, because the code that controls it (which doesn't exist yet) will monitor the clock pin. During normal operation, the RAM chip will only be enabled during Phi2. So if you stop the clock, RAMEN will not become active and the RAM chip will stay off the data bus.


Here is how I thought I would handle that.

1) Disable L-Star video and connect the pin to RAMEN

2) Host (L-Star) starts up and disables external RAM.

3) Host "disables" 65C02 by either not starting the clock or holding the clock LOW.

4) Host enables RAM and sends ROM images from EEPROM to RAM. So ROM is now RAM (which I'm fine with).

5) Host enables 65C02...perhaps forces the fake reset or something to kick start it on the external RAM which now contains Krusader, Woz, etc.

Now, how will the expansion board Propeller know the host is sending commands to it? It it monitors P25 (which *is* on the expansion port) for a transition to HIGH, then it knows the host has disabled the external RAM. One drawback to this is during the boot-up when the host holds the RAM high for a bit. Not sure how I would handle that just yet. Perhaps a small delay on the expansion board Propeller of a few milliseconds.


jac_goudsmit wrote:
The RAM chip control code (which, again, I still have to write), will make it possible to do DMA (which I think is what you're trying to do) by making it possible to enable the RAM chip for certain addresses during Phi1, and feeding STA <absolute address> instructions to the 65C02. Then all your MCU would need to do is put the data on the data bus at the right time, and wait until the clock goes high. This is all a bit "up in the air" but it will work when it's done, and it'll be documented in the source code. The module will probably be called RAMcontrol.spin, so if you see it on Github, that's probably the module you want to read.


That is pretty much what I want to do. DMA. However, I'd like the idea of running the host faster. Perhaps 2-4 MHz which we're not going to be able to do with memory.spin. So certain I/O operations would have to slow down the CPU by doing things like stopping or slowing the clock down. IMHO, this was quite common back in the day. The TMS9918 was slower than many systems that used it. But, since it could run independently of the host, certain things really benefited from it. So if my expansion board can run independently of the host, only slowing the host down when it needs certain commands, I could then do a ton of cool things.

_________________
Cat; the other white meat.


Top
 Profile  
Reply with quote  
PostPosted: Wed Feb 17, 2016 10:45 pm 
Offline
User avatar

Joined: Thu Jun 23, 2011 2:12 am
Posts: 229
Location: Rancho Cucamonga, California
cbmeeks wrote:
The external Propeller would act as a dedicated video buffer (sorta like the TMS9918) of 8-16K of video RAM. It would draw an image from its internal RAM. The host computer (L-Star) would send commands to the card for changing video RAM, drawing chars, etc.


I must admit, I never had a close look at the TMS9918 (and TMS9928/TMS9929) until today, but I would say that would be an excellent choice for a project such as this. They're still easily available via eBay (and likely other channels), and I just noticed there's a discussion about interfacing with the 6502 here.

From a quick glance at the datasheet, emulation with a Propeller shouldn't be too hard, and as a matter of fact, it looks like it's already been done.

If you have an actual chip, you can just connect it to the 65C02 bus the way you would normally connect an I/O device:
  • The databus lines connect to CD0-CD7. Note, CD0 (not CD7) is the MSB!
  • MODE connects to A0
  • Some glue logic on the address bus makes !CSR or !CSW low during Phi2, depending on R/W

I don't know how far the emulated version of the chip goes; it looks like it's part of a bigger project, so it's possible that the !CSR, !CSW and MODE lines don't simply connect to a Propeller pin. If they don't, it should be easy to modify it, though.

Either way, you will end up with the TMS chip (or emulator) in 65C02 address space at two addresses that you set by designing the glue logic. The video memory won't be accessible to the 65C02, but that's okay; it won't get in the way of other things like RAM memory or other I/O devices, and the TMS chip is probably much faster at doing cool things like graphics anyway.

Quote:
I am curious on how the Apple II did it, however. I know the video controller used the RAM during the PHI1 (LOW). But wouldn't it have to change the address bus to get the video data?


Most 6502 systems that map the video memory into the 6502 address space do the same thing: the video generator accesses the video RAM chips during Phi1 while the 6502 isn't on the data bus. If you look at the schematic of those systems, you will also see that the video RAM address bus is not connected directly to the 6502, but to a number of multiplexers (e.g. 74xx257), which switch between the CRT controller's address bus and the 6502 address bus, at the rate of the clock that also runs the 6502. At 1MHz, the CRT controller can access the video RAM exactly fast enough to generate 40 characters per row. On systems like the CBM 8032 that have 80 columns, there's some electronic trickery where the video RAM is organized so that the CPU accesses it with 8 bits at a time, but the CRT controller accesses it with 16 bits at a time.

Quote:
But...assuming 1 MHz, that is 500ns per half-cycle. So if I were 20ns into the LOW half-cycle or 400ns into the HIGH half-cycle, could I not stop it indefinitely if I wanted to? I mean, how would the CPU even know? I've always thought of the clock as time and by stopping it, the CPU has no notion of time anymore.


That's correct, you can stop the clock at any time, so if the clock is already high you can keep it high, and if the clock is already low you can keep it low. But things like reading data from the data bus happen at the transitions of the clock, and those can't be too close together in time or things inside the 6502 (or in our case, in the Propeller software) start acting together in weird ways, or won't be ready for the next clock transition.

Quote:
So if I was 20ns into the LOW half-cycle, I would need to make sure when I started the clock back up, I would run another 480ns at LOW right?


Ummmm... no. All I was saying that if you want to stop the clock in (let's say) LOW state, you have to make sure that the instructions to stop the clock have time to do so before the timer is already going high. So for example let's say the timer is 400ns into Phi1. If you want to stop the clock at that point in time, you'll have to probably execute a few assembly instructions to disable the timer and take it off the output pin. If those assembly instructions take longer than 100ns to execute, the timer will have already made the pin HIGH. If your assembly instructions take (say) 125ns to set the clock low, the Propeller will have generated a 25ns Phi2. Now let's say the 65C02 wanted to read an address that's mapped inside the Propeller using a Memory cog. The Propeller firmware has to retrieve the byte from hub memory and put it on the data bus. But by the time it puts the byte on the data bus, the clock has already gone from high to low, and that's when the 65C02 latches the data from the data bus into its internal data bus flipflops. And because there was nothing on the databus, the program won't do what it's supposed to do.

Quote:
Here is how I thought I would handle <downloading EEPROM to RAM>
...
3) Host "disables" 65C02 by either not starting the clock or holding the clock LOW.

4) Host enables RAM and sends ROM images from EEPROM to RAM.


The Propeller needs the help of the 65C02 to fill the RAM, because the 65C02 controls the address bus that determines where each byte gets stored. So I'll feed STA Absolute instructions to the 65C02, and when it thinks it's storing the accumulator into an address in the RAM chip, I let the Propeller store a different byte during Phi1. During Phi2 I keep the RAM chip disabled while this is going on. The matters of how to feed fake instructions to the 65C02 have already been solved, though I don't have time to elaborate on them right now. It will probably work in a similar way as the fake reset code that's in the memory cog now.

There's another problem by the way: the EEPROM and the 65C02 share the same Propeller pin as clock. It will be necessary to connect the RDY line to another pin so that the 65C02 won't execute instructions while the Propeller is playing with the EEPROM.

Quote:
Now, how will the expansion board Propeller know the host is sending commands to it? It it monitors P25...


For expansion boards, you really should try to stay in the world of the 65C02. It's always possible to go from the 65C02 world to the world of any I/O devices and microcontrollers that are attached to the bus, but because the 65C02 has control over the address bus, the reverse is more difficult and you will need help from the 65C02.

Yes, it's possible to use P25-P27 to control any devices on an expansion board. In fact, ALL Propeller I/O pins are connected to the expansion board so you have maximum flexibility. But you have to keep in mind that the 65C02 and the RAM chip are also connected to those pins at the same time!

So the easiest way for the system to do I/O is in the traditional way known to all 6502 computers: map the I/O in one or more memory locations, and use 6502 assembly (or C or whatever) to access those locations to get to the I/O data. Sure, if you don't need a keyboard or video you have 3 pins that you can use to communicate with the expansion bus (but actually more likely you'll have one or two pins free because a sophisticated system will probably need to have the main board Prop connected to RAMEN and RDY) or maybe even none at all (if the main Prop needs NMI to initiate a memory transfer).

In other words: you shouldn't count on having P25 (or any other pin) available for any magic, but you don't have to: one 74HC138 chip connected to the address bus is enough to generate chip-enable-not signals for up to 8 devices that are 256 bytes apart in high memory space. Read Garth Wilson's pages for more information.

Quote:
I'd like the idea of running the host faster. Perhaps 2-4 MHz which we're not going to be able to do with memory.spin.


I think it's probably possible to speed up the system to 2 or maybe 4 MHz and slowing down the 65C02 down whenever it talks to the Propeller. I think because of the discussed problems with stopping and restarting the clock, it might be easier to do this by controlling RDY. The main problem there is that the RDY control cog will only have one or two instructions to decide whether to slow down, and then it still has to do it. I'll have to do some more research for that, it's not at the top of the list of priorities. Let's get some (more) cool things working at 1MHz or (with a 6.25MHz crystal) 1.25MHz.

===Jac


Top
 Profile  
Reply with quote  
PostPosted: Wed Feb 17, 2016 11:23 pm 
Offline
User avatar

Joined: Wed Aug 17, 2005 12:07 am
Posts: 1250
Location: Soddy-Daisy, TN USA
jac_goudsmit wrote:
I must admit, I never had a close look at the TMS9918 (and TMS9928/TMS9929) until today, but I would say that would be an excellent choice for a project such as this. They're still easily available via eBay.....


Yes, it's an awesome video chip. Especially since it was designed and sold in the late 70's. Take that nvidia!

I'm also familiar with the Propeller emulation of it. :-)

I was mostly using the TMS as an example. The video driver I'd like to use is actually much simpler (similar to the ULA of the ZX Spectrum).

I actually have several of the real TMS chips. But I didn't want to install one of them because they are pretty tricky to get going from what I understand. Mainly because of the odd 10.xxxx MHz crystal it needs isn't common. I have a few of them but they were pricey.

jac_goudsmit wrote:
That's correct, you can stop the clock at any time, so if the clock is already high you can keep it high, and if the clock is already low you can keep it low. But things like reading data from the data bus happen at the transitions of the clock, and those can't be too close together in time or things inside the 6502 (or in our case, in the Propeller software) start acting together in weird ways, or won't be ready for the next clock transition.


OK, that makes more sense. I wasn't thinking of the transitions of the cycles.


jac_goudsmit wrote:
There's another problem by the way: the EEPROM and the 65C02 share the same Propeller pin as clock. It will be necessary to connect the RDY line to another pin so that the 65C02 won't execute instructions while the Propeller is playing with the EEPROM.


AH! That's right. Dur. I need the clock in order to use the EEPROM but I'm stopping the clock to disable the 65C02. Catch 22 and I can't do that. Oh well.. LOL


jac_goudsmit wrote:
For expansion boards, you really should try to stay in the world of the 65C02. It's always possible to go from the 65C02 world to the world of any I/O devices and microcontrollers that are attached to the bus, but because the 65C02 has control over the address bus, the reverse is more difficult and you will need help from the 65C02.


Yeah, I think you're right. I actually don't know why I didn't think of that! My software mindset usually starts with a solution (Propeller) looking for a problem (color video). It's hard to switch that off sometimes.

Well, I'm not discouraged. In fact, I've learned even more. Thanks. :-)

So, stepping back...a better idea for an A/V board for the L-Star would be to simply send data via the add/data bus like normal I/O.

Perhaps I should write a "video.spin" cog that would be similar to the PIA cog but for sending data to the board I/O?

Thanks!

_________________
Cat; the other white meat.


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

All times are UTC


Who is online

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