6502.org Forum  Projects  Code  Documents  Tools  Forum
It is currently Sat Nov 23, 2024 3:49 am

All times are UTC




Post new topic Reply to topic  [ 72 posts ]  Go to page Previous  1, 2, 3, 4, 5  Next
Author Message
PostPosted: Sun Feb 16, 2014 9:18 pm 
Offline
User avatar

Joined: Wed Feb 13, 2013 1:38 pm
Posts: 589
Location: Michigan, USA
jac_goudsmit wrote:
Michael wrote:
In order to load RAM during start-up, the PIC puts the R65C02 through the 'reset' process, then pushes the $FFFC and $FFFD reset vector bytes to the R65C02, then pushes instruction and operand bytes that make up a simple three instruction LDA/STA/JMP loop which guides the R65C02 through the process of filling RAM memory.

It would probably be a lot more efficient if you would use the same process as my Propeddle project: After giving the 6502 the reset vector (which is the start of memory), just write the data to memory from the PIC during the first half of the clock cycle when the 6502 doesn't use the databus, and feed the 6502 a fake instruction during the second half of the clock cycle.
Wouldn't that require an additional pin to control the R/W line on the RAM? Not to mention additional 'overhead' in terms of PIC instruction cycles to manage it? It seems like you've come up with a very nice method. Unfortunately, I don't think I have the spare resources necessary to duplicate it.

I could probably optimize the loader and run it at 2-MHz but the loader function is not really where I need to concentrate my efforts right now.

Quote:
I actually use NMI to load data to RAM, so I can do it anytime while the system is running too. I feed a fake address for the NMI vector, and access the RAM from my Propeller while the 6502 clock is low and it doesn't use the databus. Then I take the RAM off the data bus and feed the 6502 with $C9 bytes.
That's an interesting approach. Are there times when you need to read/write RAM other than during start-up? I know I'll be reading and writing a 512 byte sector buffer when I implement the SPI interface to an SD Card but I was going to let the R65C02 do all the work by giving it direct access to the high-speed SPI peripheral registers inside the PIC.

Quote:
Unlike the NOP instruction which is 2 clocks and one byte (so the address bus advances only once every OTHER clock cycle), $C9 is CMP Immediate which is 2 clocks, 2 bytes (the 6502 sees it as CMP #$C9) so the address bus increases on EVERY clock cycle. That means that I can store one byte of memory on every clock cycle!

The CMP Immediate instruction changes the flags register but the flags got saved on the stack by the NMI interrupt handling anyway. I'm planning on intercepting the saving of the flags too, so that the NMI leaves no trace in memory at all, and so there's no chance of a stack overflow either. At the end of the memory area, I feed a $40 (RTI) so the 6502 reads the flags back and continues where it left off. My Propeller then resumes "normal" operation where the 6502 has access to the RAM.

And of course, the same process can be used to let the Propeller READ data from the RAM chip.

I think you have a very well thought out and well designed system.

Take care. Have fun. Cheerful regards, Mike


Top
 Profile  
Reply with quote  
PostPosted: Mon Feb 17, 2014 12:15 am 
Offline
User avatar

Joined: Thu Jun 23, 2011 2:12 am
Posts: 229
Location: Rancho Cucamonga, California
Michael wrote:
Wouldn't that require an additional pin to control the R/W line on the RAM?


Yes. In my system, the Propeller (not the 6502) controls the !OE and !WE lines of the RAM chip so I can write to the RAM even when R/W is high and I can access the RAM even when Phi2 is low. I see now that with your schematic, this is not an option, and I understand how you don't want to make that change to the code and the schematic. It would actually take two extra pins by the way: one extra pin for the RAM chip and one pin to pull NMI down on the 6502.

Quote:
That's an interesting approach. Are there times when you need to read/write RAM other than during start-up?


I was inspired by Dennis Ferron's Prop-6502 to use this method, and he used the Reset line because why not -- after all the Propeller is going through an initialization sequence already anyway so might as well make the download part of the reset sequence. But he used NOP instructions to make the 6502 generate increasing address values, and I wanted to find another instruction that was 2 bytes instead of one byte, so the address increments would go twice as fast. As it turns out, I couldn't think of any instruction that doesn't have any sort of side effect and is 2 bytes, and 2 clock cycles. But there are several instructions that only affect the flags register and no others, and if I would use NMI, the 6502 would automatically save and restore the flags as part of the interrupt handling.

Plus, using NMI fixed a few other potential concerns: First of all, during a reset sequence it's possible for the processor to get interrupted, and this is not possible during an NMI, at least if I keep the NMI low until the RTI happens(*). So I don't have to worry about other hardware also generating an NMI and confusing things.

The only reason I can think of to access the SRAM while the system is running, is to save/restore a snapshot of memory which is a feature that's not really high on my must-haves. But using NMI instead of RESET solves some other problems and the ability to access memory directly not only during, but also after setup, pretty much gets thrown in for free. So even if, for now, I can't think of a compelling reason to access the RAM using NMI instead of RESET, the advantages in my case are pretty clear.

===Jac

(*) The NMI line is edge-triggered so an interrupt only gets generated when the signal goes from high to low. If the MOS Technology engineers would have made NMI level-triggered (like IRQ), the NMI would keep interrupting itself because even though the interrupt-flag gets set when the interrupt routine starts, NMI is non-maskable.


Top
 Profile  
Reply with quote  
PostPosted: Mon Feb 17, 2014 5:43 am 
Offline
User avatar

Joined: Wed Feb 13, 2013 1:38 pm
Posts: 589
Location: Michigan, USA
jac_goudsmit wrote:
Michael wrote:
Wouldn't that require an additional pin to control the R/W line on the RAM?
Yes. In my system, the Propeller (not the 6502) controls the !OE and !WE lines of the RAM chip so I can write to the RAM even when R/W is high and I can access the RAM even when Phi2 is low. I see now that with your schematic, this is not an option, and I understand how you don't want to make that change to the code and the schematic. It would actually take two extra pins by the way: one extra pin for the RAM chip and one pin to pull NMI down on the 6502.

Perhaps this is a good example of different design philosophies for similar goals?

My goal was to eliminate ROM in favor of RAM, which requires a method for initially loading RAM, while keeping the number of I/O pins required to support that method to a minimum. I found that if I control the reset and clock lines (3 pins), and if I can push instructions to the R65C02 (8 pins) while controlling the decoder (0 pins), that tasking the R65C02 to load RAM works quite well.

My design philosophy/strategy is; simplicity, elegance, and performance. I use a very limited set of capabilities and features on the PIC to complement the R65C02 while leaving the R65C02 in control rather than the other way around.

Why take control of the R/W pin away from the R65C02 when I can task the R65C02 to perform the operations I need? In this case, I'm simply loading RAM after a reset. This isn't a time critical task and it's difficult for me to justify the resources required to support some other unnecessarily exotic method. It's also worth noting that taking control of the R/W pin implies additional overhead during each 6502 cycle which would impact performance in the main loop and it might become difficult to maintain a 1-MHz clock for the R65C02.

Quote:
Quote:
That's an interesting approach. Are there times when you need to read/write RAM other than during start-up?
I was inspired by Dennis Ferron's Prop-6502 to use this method, and he used the Reset line because why not -- after all the Propeller is going through an initialization sequence already anyway so might as well make the download part of the reset sequence.

I agree that the reset button plays a key role in all our designs and it's the perfect point at which to load RAM. It's also the point where I offer the user the option to enter the PIC "supervisor" instead of auto-booting into the R65C02.

While the PIC "supervisor" is meant to provide a convenient method for uploading a new "decoder map" or ROM image files into flash memory and for selecting a default PIC I/O method, an enhanced version of the "supervisor" for an "educational" or "starter" SBC might include a mini assembler, a dis-assembler, a memory dump command, and single-step with register view/edit capabilities. None of these capabilities would require code in R65C02 RAM, nor would they require the use of the R65C02 NMI pin since we have the capability to push any series of instructions to the R65C02 that we want. For example, use one series of instructions to preserve registers and grab the pc, use another series to read or write a memory location, use another series to restore registers and the pc, use another series to execute a single instruction, use another series to update the LED, LCD, or serial display and perhaps poll a keypad. The possibilities are endless and I had a lot of fun experimenting with many of these capabilities early in the development cycle. Combining them into a comprehensive, cohesive, intuitive, aesthetically pleasing, and just plain useful user interface/experience however will be a challenge.

Quote:
So even if, for now, I can't think of a compelling reason to access the RAM using NMI instead of RESET, the advantages in my case are pretty clear.

Sounds great. At the risk of sounding patronizing, I think you've done extremely well with the limited number of pins you have to work with on the Propeller. Keep up the good work.

Take care. Regards, Mike


Last edited by Michael on Mon Feb 17, 2014 5:32 pm, edited 10 times in total.

Top
 Profile  
Reply with quote  
PostPosted: Mon Feb 17, 2014 10:21 am 
Offline
User avatar

Joined: Tue Mar 02, 2004 8:55 am
Posts: 996
Location: Berkshire, UK
Mike,

How about using INC absolute to take an instruction out of your load loop. In the memory read phase of the INC provide the ROM byte minus one to the 6502 and in the write phase allow the 6502 to write it back (plus one) to the RAM chip.

_________________
Andrew Jacobs
6502 & PIC Stuff - http://www.obelisk.me.uk/
Cross-Platform 6502/65C02/65816 Macro Assembler - http://www.obelisk.me.uk/dev65/
Open Source Projects - https://github.com/andrew-jacobs


Top
 Profile  
Reply with quote  
PostPosted: Mon Feb 17, 2014 1:40 pm 
Offline
User avatar

Joined: Wed Feb 13, 2013 1:38 pm
Posts: 589
Location: Michigan, USA
BitWise wrote:
Mike,

How about using INC absolute to take an instruction out of your load loop. In the memory read phase of the INC provide the ROM byte minus one to the 6502 and in the write phase allow the 6502 to write it back (plus one) to the RAM chip.


Hi Andrew,

Thanks for the idea, but, even though that reduces 6502 code to two instructions (an INC <abs> and a JMP <abs>) and six bytes, it still takes 9 cycles so my loader loop will still have 9 push() operations. No net savings, in terms of size and/or speed.

Regards, Mike

Code:
  /*                                                                *
   *  copy code in PIC flash to 6502 RAM (decoder on)               *
   *                                                                */
     tblptrl = 0x00;            // point to rom image
     tblptrh = 0xE0;            // at E000..FFFF (8K)
     tblptru = 0x00;            //
     do                         // copy rom image to ram
     { loader(0xA9);            // R DF00 A9, LDA #$nn
       asm tblrd*               //
       loader(tablat);          // R DF01 nn
       loader(0x8D);            // R DF02 8D, STA $hhll
       loader(tblptrl);         // R DF03 ll, aaaa%256
       loader(tblptrh);         // R DF04 hh, aaaa/256
       loader(tblptrh);         // W hhll nn, write op
       loader(0x4C);            // R DF05 4C, JMP ioloc
       loader(io_lo);           // R DF06 00
       loader(io_hi);           // R DF07 DF
       tblptrl++;               // bump pointer
       if(tblptrl == 0)         //
       { tblptrh++;             //
         put232('.');           //
       }                        //
     } while(tblptrh);          // until rollover from 0xFFFF


Top
 Profile  
Reply with quote  
PostPosted: Wed Feb 19, 2014 2:27 pm 
Offline
User avatar

Joined: Fri Dec 11, 2009 3:50 pm
Posts: 3367
Location: Ontario, Canada
jac_goudsmit wrote:
But he used NOP instructions to make the 6502 generate increasing address values, and I wanted to find another instruction that was 2 bytes instead of one byte, so the address increments would go twice as fast. As it turns out, I couldn't think of any instruction that doesn't have any sort of side effect and is 2 bytes, and 2 clock cycles.
Jac, it sounds like you should consider using one of the 32 identical "nop" instructions that fill columns _3 and _B on the 'C02 opcode map. These are one-byte, one-cycle nop instructions that don't affect the flags. They can be used for all sorts of tricks! (Example: Ultra-fast output port) And they're ideal for generating increasing address values (for boot loading, or for a DMA-like "cheap video" video interface that uses minimal hardware).

(Because STP and WAI opcodes reside in the columns mentioned, the WDC 'C02 has 30 of these instructions, not 32. But you only need one!)

cheers
Jeff

_________________
In 1988 my 65C02 got six new registers and 44 new full-speed instructions!
https://laughtonelectronics.com/Arcana/ ... mmary.html


Top
 Profile  
Reply with quote  
PostPosted: Wed Feb 19, 2014 3:51 pm 
Offline
User avatar

Joined: Thu Jun 23, 2011 2:12 am
Posts: 229
Location: Rancho Cucamonga, California
Dr Jefyll wrote:
Jac, it sounds like you should consider using one of the 32 identical "nop" instructions that fill columns _3 and _B on the 'C02 opcode map. These are one-byte, one-cycle nop instructions that don't affect the flags.


I don't remember seeing in the WDC docs that they are one-byte one-cycle; I assumed they would be two cycles; that's interesting and I guess I should review...

I wouldn't want to use an instruction that's not present on all types of 6502, though; I want to make it compatible with as many different types of 6502 as possible, so that someone could eventually even make e.g. a 1541 drive emulator out of my kit (the firmware of the 1541 uses an undocumented opcode on the MOS 6502 if I'm not mistaken). For my purpose, one-clock one-cycle is just as efficient as two-clock two-cycle.

Thanks for the tip!

===Jac


Top
 Profile  
Reply with quote  
PostPosted: Wed Feb 19, 2014 4:03 pm 
Offline
User avatar

Joined: Wed Feb 13, 2013 1:38 pm
Posts: 589
Location: Michigan, USA
Hey, Jeff. That really is genius! Thank you.

Regards, Mike


Top
 Profile  
Reply with quote  
PostPosted: Wed Feb 19, 2014 5:37 pm 
Offline
User avatar

Joined: Fri Dec 11, 2009 3:50 pm
Posts: 3367
Location: Ontario, Canada
Glad I was able to share an item of interest with you guys. My own experience is with the Rockwell 'C02. (I had to document all the undefined NOP's before building my Kimklone years ago.) As for the WDC '02, I expect its undefined NOP's would behave identically to the Rockwell's. It's my understanding the two chips share a common origin -- they are not independent designs.

jac_goudsmit wrote:
For my purpose, one-clock one-cycle is just as efficient as two-clock two-cycle.
Yes, and your choice of CMP Immediate runs on all versions of the 6502, as you say. For your needs, the only (slight) advantage offered by the one-cycle NOP's is preserving the flags.

-- Jeff

_________________
In 1988 my 65C02 got six new registers and 44 new full-speed instructions!
https://laughtonelectronics.com/Arcana/ ... mmary.html


Top
 Profile  
Reply with quote  
PostPosted: Wed Feb 19, 2014 6:04 pm 
Offline
User avatar

Joined: Thu Jun 23, 2011 2:12 am
Posts: 229
Location: Rancho Cucamonga, California
I read the Rockwell 65C02 datasheets. One of the nice things is that, even though they presumably still have the undocumented instructions as the NMOS 6502, it specifies explicitly that you can hold the chip indefinitely while the clock is HIGH. This inspired me to start and end my main loop with CLK0=HIGH too: when the main loop isn't running, the 6502 is held in a state where at least the Rockwell 65C02 can be held indefinitely (and an NMOS 6502 can be held for some time with CLK0=high).

Later on in the development, I realized that I could use this in combination with the fact that the outputs on the Propeller are OR'ed from all cogs. There's not enough time in a 1 microsecond cycle to check a flag in hub memory, in order to see whether another cog wants to stop the main loop (e.g. for debugging). But it IS possible for the main control cog to drop CLK0 low and then check if another cog is still pulling it high, and break out of the loop if so. It's a pretty nifty idea even if I do say so myself :-)

Which reminds me, I really should finish that "Theory of Operations" article on my website :-)

===Jac


Top
 Profile  
Reply with quote  
PostPosted: Wed Feb 19, 2014 7:22 pm 
Offline
User avatar

Joined: Thu May 28, 2009 9:46 pm
Posts: 8507
Location: Midwestern USA
jac_goudsmit wrote:
I don't remember seeing in the WDC docs that they are one-byte one-cycle; I assumed they would be two cycles; that's interesting and I guess I should review...

There are no single cycle instructions in the W65C02S and W65C816S. Illegal instructions in the 65C02 are NOPs that can be of varying length and execution time. As WDC has recently changed foundries, it's conceivable that the behavior of illegal instructions could change as well.

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


Top
 Profile  
Reply with quote  
PostPosted: Wed Feb 19, 2014 8:08 pm 
Offline
User avatar

Joined: Wed Feb 13, 2013 1:38 pm
Posts: 589
Location: Michigan, USA
jac_goudsmit wrote:
... I really should finish that "Theory of Operations" article on my website :-)

Hi Jac,

I would love to see that, especially if it describes how you've allocated the various cogs to various tasks (with a brief description of the task). I've never looked at your source code because I wasn't familiar with Propeller Spin or Assembler (which I hope to remedy soon) so I can only guess at how you're doing things.

Cheerful regards, Mike


Top
 Profile  
Reply with quote  
PostPosted: Wed Feb 19, 2014 8:25 pm 
Offline
User avatar

Joined: Wed Feb 13, 2013 1:38 pm
Posts: 589
Location: Michigan, USA
jac_goudsmit wrote:
Later on in the development, I realized that I could use this in combination with the fact that the outputs on the Propeller are OR'ed from all cogs. There's not enough time in a 1 microsecond cycle to check a flag in hub memory, in order to see whether another cog wants to stop the main loop (e.g. for debugging). But it IS possible for the main control cog to drop CLK0 low and then check if another cog is still pulling it high, and break out of the loop if so. It's a pretty nifty idea even if I do say so myself :-)

I assume you're talking about stopping the clock after you've started running the 65C02 at full speed? If so, then under what conditions would a cog want to stop the main loop?

Mike


Top
 Profile  
Reply with quote  
PostPosted: Wed Feb 19, 2014 8:42 pm 
Offline
User avatar

Joined: Fri Dec 11, 2009 3:50 pm
Posts: 3367
Location: Ontario, Canada
BigDumbDinosaur wrote:
There are no single cycle instructions in the W65C02S and W65C816S.
May I ask what makes you so certain of this? I'll grant the datasheet says the NOPs can be of varying length and execution time, but, to my reading, that hardly excludes an execution time of one cycle. I hope you can cite a source other than your own surmise.

My own information comes from direct observation of the Rockwell '02 actually running these one-cycle NOP's. I suspect the WDC '02 will show the same behavior, but I have not confirmed this.

jac_goudsmit wrote:
I read the Rockwell 65C02 datasheets. One of the nice things is that, even though they presumably still have the undocumented instructions as the NMOS 6502
Jac, I have a feeling you wrote that in a hurry. :D I'm sure you know the undefined opcodes on the NMOS 6502 are not NOP's.

cheers
Jeff

_________________
In 1988 my 65C02 got six new registers and 44 new full-speed instructions!
https://laughtonelectronics.com/Arcana/ ... mmary.html


Last edited by Dr Jefyll on Wed Feb 19, 2014 8:43 pm, edited 1 time in total.

Top
 Profile  
Reply with quote  
PostPosted: Wed Feb 19, 2014 8:43 pm 
Offline
User avatar

Joined: Thu Jun 23, 2011 2:12 am
Posts: 229
Location: Rancho Cucamonga, California
Michael wrote:
I assume you're talking about stopping the clock after you've started running the 65C02 at full speed? If so, then under what conditions would a cog want to stop the main loop?


The answer is pretty much the same as to the question you asked earlier: "Why would I want to access the SRAM chip after I've already started the 6502?"

I want to think of Propeddle not only as a 6502 computer (either a newly developed system or emulating an existing system), but also as a 6502 development system. Making it possible to stop the 6502, to debug the code or do things such as reading the contents of the RAM to make a backup is a no-brainer for a development system. It really didn't even come up in my mind why it might NOT be needed. :-)

===Jac


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

All times are UTC


Who is online

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