6502.org Forum  Projects  Code  Documents  Tools  Forum
It is currently Fri Nov 22, 2024 12:49 pm

All times are UTC




Post new topic Reply to topic  [ 321 posts ]  Go to page Previous  1 ... 3, 4, 5, 6, 7, 8, 9 ... 22  Next
Author Message
PostPosted: Sat Apr 08, 2017 8:28 pm 
Offline
User avatar

Joined: Thu Dec 11, 2008 1:28 pm
Posts: 10985
Location: England
Quote:
There's really no point in testing the RAM. If it doesn't work, you won't even be able to run the self test unless you can go with no variables and no stack use (JSR/RTS) etc.. It's not like a disc which may have bad sectors. The manufacturer tests every byte before selling the RAM. If there's any damage after that, it will be to the input or output circuitry that affects everything, not just a random byte here and there. You could test to see how much memory is installed, but there's no need to test every byte for that, just the ones on likely boundaries. You could also just tell it with jumper options, or just put the quantity in the ROM, and re-program the ROM in the unlikely event that you ever change the amount of memory.


Hmm, if it's a single-chip RAM, then I'd mostly agree - although any kinds of faults in the address bus wiring could cause trouble. Maybe they'd always show up as making the RAM always appear smaller than it should, but I'm not sure of that (what about two address lines shorted? Or a floating address line?)

But if you have a RAM system with more than one chip, there are more possibilities - when testing a retro system, you might well have 8 bit wide RAMs, or two nibble wide RAMs, or several banks of either of those. And in this case, the types of errors you get could be more subtle than just "it doesn't work."

See this recent adventure as an example.


Top
 Profile  
Reply with quote  
PostPosted: Sat Apr 08, 2017 9:08 pm 
Offline
User avatar

Joined: Thu May 28, 2009 9:46 pm
Posts: 8505
Location: Midwestern USA
Dan Moos wrote:
Ok, I'd like my monitor to have a sort of "system self test" function, and I was contemplating what would make fore a good memory self test.

I think you may be confusing POST (power-on self-test) with a monitor. POST gets conducted well before a monitor or other user-interactive environment is started.

Quote:
My notion is to write a byte to memory, read it back, and compare it to some value in the X or Y register. Maybe just use X as a counter, load the current count into the memory address under test, read it back into the accumulator, and compare it to the X register, and so on through memory.

Sound good?

Here's the procedure I use in my POC units' firmware:

  1. Destructively test direct (zero) page. The process writes two different bit patterns (%01011010 and %10100101) into each location, wastes some time and then compares the cell being tested to the test pattern. If a mismatch occurs the system is halted. You can use <dp>,X addressing for this test.

  2. Destructively test the stack. The test on each memory cell is the same procedure as in 1. above.

  3. Configure your UART so you can write to your console.

  4. Print a POST banner to the screen to let you know you made it through the direct page and stack tests, as well as the UART configuration.

  5. Non-destructively test all RAM above the top of the stack, that is, RAM from $0200 up. I used the following procedure in POC V1, although with some minor differences due to the 65C816 being in 16 bit native mode:

    1. Set up a 16 bit pointer somewhere in direct page and initialize it to $0200. This will be the address of the cell currently undergoing test.
    2. Set up a 16 bit counter somewhere in direct page and also initialize it to $0200. This will be a count of the total contiguous RAM that has been tested. The value $0200 accounts for the size of direct page and the stack.
    3. Read the cell under test and store that value in a safe place. Direct page is a suitable storage location, since your first test demonstrated that direct page can be successfully read and written. You can use (<dp>) addressing, which is supported by the 65C02 (but not the NMOS 6502).
    4. Write your first test value, such as %01011010, to the cell under test and waste some time.
    5. Compare the cell under test to your test value and handle as required if a mismatch is detected.
    6. Repeat steps 5.4 and 5.5, but with the second test value, e.g., %01011010.
    7. If both tests of the cell are successful restore the value saved in step 5.3, above.
    8. Increment the 16 bit total RAM counter. If desired, translate the total RAM counter to a human-readable form and display it on your console. This will produce a visible indication that testing is progressing. In the interest of performance, you should only update the count every 256 cells tested, that is, when the count's least significant byte is $00.
    9. Increment the 16 bit direct page pointer. Testing is completed if this pointer has reached the first non-RAM address in your system. Otherwise, loop back to step 5.3 above.

    In the event of a RAM error during the absolute phase of the test the address at which failure occurred will be in your direct page pointer. You could output a diagnostic to the console and indicate at which address failure occurred. This is possible because you had already gotten your console running before starting the absolute RAM test.

  6. Configure your remaining I/O hardware as necessary.

  7. Start your monitor or other software.

That is essentially what POST is all about. There are more detailed RAM tests than just writing a couple of bit patterns, such as walking bit, rotate and invert, etc. However, what I described is more than adequate for hobby purposes.

Having said all that, the reality is that static RAM seldom fails once installed and used. Occasional "infant mortality" failures do occur, but if the RAM survives more than a couple of hours of continuous operation it is unlikely to fail unless spiked or otherwise electrically over-stressed.

GaBuZoMeu wrote:
RAM tests are one way to spent really huge amount of processing time...

The test I described will run in a fraction of a second, generally with little to no perceptible delay to the user, unless the MPU is clocked really slowly. Now, in the case of a 65C816 system with a lot of RAM, yes, a lot of processing time will get consumed. But I find it kind of neat to watch POC V2 plow through a meg of RAM.

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


Top
 Profile  
Reply with quote  
PostPosted: Sat Apr 08, 2017 9:20 pm 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8543
Location: Southern California
BigEd wrote:
Hmm, if it's a single-chip RAM, then I'd mostly agree - although any kinds of faults in the address bus wiring could cause trouble. Maybe they'd always show up as making the RAM always appear smaller than it should, but I'm not sure of that (what about two address lines shorted? Or a floating address line?)

True, but those things are matters of troubleshooting the design and construction, not the RAM. Once it's correct, it will continue to be. Modern RAMs themselves are super reliable (which may not have been the case 40 years ago). There's no need to test it every time you boot up.

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


Top
 Profile  
Reply with quote  
PostPosted: Sat Apr 08, 2017 11:02 pm 
Offline

Joined: Sat Mar 11, 2017 1:56 am
Posts: 276
Location: Lynden, WA
You guys are missing the main idea. This is a hobby. There's no "point" to any of this. :wink: Just me teaching myself how to do this stuff.

Besides, the thing currently resides on a breadboard. A RAM test is a pretty pain free way to test practically every connection on the thing. Every address line, every data line, and some important control lines are involved. If i have he thing report what address it was testing when it failed, it would narrow down what wires to investigate. This is another reason to make it a monitor command, and not just a onetime POST routine.

But, yeah, main reason: Just for the hell of it 8)


Top
 Profile  
Reply with quote  
PostPosted: Sat Apr 08, 2017 11:44 pm 
Offline

Joined: Sat Mar 11, 2017 1:56 am
Posts: 276
Location: Lynden, WA
Dumb question. The WDC datasheets use a pin labeling convention I've never encountered before. Active LOW pins are suffixed with a 'B', rather than having a bar over the pin name like I'm used to seeing. For instance, 'RSTB'

What does the 'B' actually stand for? Is it completely equivalent to the bar, or is there more to it?


Top
 Profile  
Reply with quote  
PostPosted: Sun Apr 09, 2017 12:05 am 
Offline

Joined: Sat Mar 11, 2017 1:56 am
Posts: 276
Location: Lynden, WA
Another dumb question. I am reading Garth's interrupt primer, specifically the section on RS232 usage, and have a question about how he reserved memory for the Rx buffer.

He uses a .DFS directive to set aside memory for the buffer.

Question 1: this is the same as the .DS directive that the Kowalski assembler uses, right?

Question 2: there doesn't seem to be any starting point in RAM mentioned. For instance, the buffer itself is set aside with:

Code:
BUFFER: .DFS    $100


Where specifically in RAM does this buffer reside? How do you tell it where?


Top
 Profile  
Reply with quote  
PostPosted: Sun Apr 09, 2017 1:25 am 
Offline
User avatar

Joined: Wed Mar 01, 2017 8:54 pm
Posts: 660
Location: North-Germany
Most probably .DFS is equivalent to .DS which stands for Define Storage. Some assemblers use RMB (Reserve Memory Byte) for this purpose.

You can place a buffer by a preceding .org like:
Code:
                                  .org $address_where_the_buffer_should_reside
Buffer_Label_for_later_reference: .DS  size_of_buffer
; to verify the buffer length you may add
Buffer_End_plus_one:              .equ *
The last line should give you the next free address past your buffer - say you start at $1000 and your buffer should be 1000 (decimal) byte big, the resulting address should be $1000+1000 = $13E8.

As some 6502 assembler recognizes " * = $1234" as .org $1234 you may use "buffer *=*+100 " to reserve 100 bytes.

--------

Why WDC uses XYZB to indicate signal XYZ is low active I cannot say. Perhaps B for bar?


--------

Trying to verify your wiring would be nearly impossible: if wires from the CPU to whatever fails, no program would ran. If wires to the ROM would fail - same. If wires to/from address decoder fails - same. If wires to I/O fails - you get no messages or the computer did not respond (and your RAM check wouldn't issue an error).
Only if wiring from/to RAM would fail, you might get an error message - but it requires, that all of your software to do this (incl. issuing the message) didn't require any RAM (remember JSR requires a working stack). You got it?


Top
 Profile  
Reply with quote  
PostPosted: Sun Apr 09, 2017 1:44 am 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8543
Location: Southern California
WDC is the only manufacturer I know of that puts "B" on the end in place of the overbar. I'm sure it stands for "bar," but they never tell us.

"DFS" in the Cross-32 assembler stands for "DeFine Storage," so it reserves the specified number of bytes, starting at whatever address the program counter has at that point in the assembly. The contents are not specified yet, and the assumption is that they will be written to this variable or array at run time. The value of the expression is added to the program counter, and assembly continues. Except the changed value of the program counter, no values are written to the hex output file.

It doesn't matter where you put the buffer, as long as it's in RAM, not ROM. If the program goes into ROM, you'll usually want to define all the RAM variable storage before all the ROM programming. For example:
Code:
         ORG  0      ; (or other ZP variable base address)
VAR1:    DFS  1      ; example of a 1-byte variable
VAR2:    DFS  1      ; another 1-byte variable
TIMER:   DFS  4      ; now a 4-byte one
VARxyz:  DFS  2      ; and then another 2-byte one
   <etc.>            ; whatever--you get the idea.


         ORG  $200    ; (to get past the page-1 stack area)
   <more variables and arrays>

BUFFER:  DFS  $100   ; Reserve 256 bytes of RAM for the buffer ring itself.
RD_PTR:  DFS  1      ; Reserve one byte of RAM for the read pointer
WR_PTR:  DFS  1      ; and one for the write pointer.
   <more variables and arrays>


         ORG  $8000  ; (or wherever your ROM starts)
RESET:   <start your ROM code here.  Change the label as appropriate.>

If the code goes into RAM too, then the variables can be mixed in among the routines (or even be in instructions' operands in self-modifying code! :D ) As long as the variable is in RAM, it doesn't matter what its address is. The assembler takes that burden off of you. The assembler doesn't care what the contents are, and you don't care what the address is.

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


Top
 Profile  
Reply with quote  
PostPosted: Sun Apr 09, 2017 1:59 am 
Offline

Joined: Sat Mar 11, 2017 1:56 am
Posts: 276
Location: Lynden, WA
So I can use .ORG to set aside RAM? I thought it was just for placing code elements. So if I have a program destined for ROM, I can use .ORG to set aside RAM?

I surely wished that assembly directives were given the same amount of time in books that the actual mnemonics are. That and the complete non-standardization of how such directives should look.

Another Primer topic Garth? What I wouldn't give for a focused tutorial on assembler directives, including a list of all the various equivalent directives that are syntaxed differently across various assemblers. You know. ".ORG does blah blah blah... other assembler's varient's are =*,ect"

This one topic has been my biggest hurdle in learning 6502 assembly. I kinda understand how things could have gotten so non-standardized over the years, but it still sucks :roll:


Top
 Profile  
Reply with quote  
PostPosted: Sun Apr 09, 2017 2:12 am 
Offline

Joined: Sat Mar 11, 2017 1:56 am
Posts: 276
Location: Lynden, WA
I have another programing question.

It involves indirect addressing and strings.

Lets say I have some pre-canned messages I've placed in ROM.

Lets say I have:

Code:
error_message:     .BYTE   "Why would you type such a foolish thing", $00


Also lets say I have a subroutine who's job is to output strings. So I'd have some memory location where I'd stash the address of the desired string (my error message in this case) before jmp'ing to the subroutine. Basically I need a pointer.

My books allude to the indexed address modes being the way to do pointers, but as far as I can tell, you are stuck using 0 page as the location for your pointers. Not that that's a problem I guess. I'd only need 2 bytes for my whole messaging system.

How is this typically done?


Top
 Profile  
Reply with quote  
PostPosted: Sun Apr 09, 2017 2:16 am 
Offline
User avatar

Joined: Wed Mar 01, 2017 8:54 pm
Posts: 660
Location: North-Germany
Dan, if you spent some time reading http://6502.org/documents/datasheets/wdc/wdc_65816_programming_manual.pdfyou can find some common as well as some less usual directives (instructions for the assembler) explained. It starts at page 66.


Top
 Profile  
Reply with quote  
PostPosted: Sun Apr 09, 2017 2:27 am 
Offline
User avatar

Joined: Wed Mar 01, 2017 8:54 pm
Posts: 660
Location: North-Germany
Dan Moos wrote:
I have another programing question.

It involves indirect addressing and strings.

Lets say I have some pre-canned messages I've placed in ROM.

Lets say I have:

Code:
error_message:     .BYTE   "Why would you type such a foolish thing", $00


Also lets say I have a subroutine who's job is to output strings. So I'd have some memory location where I'd stash the address of the desired string (my error message in this case) before jmp'ing to the subroutine. Basically I need a pointer.

My books allude to the indexed address modes being the way to do pointers, but as far as I can tell, you are stuck using 0 page as the location for your pointers. Not that that's a problem I guess. I'd only need 2 bytes for my whole messaging system.

How is this typically done?

Using a pointer (must be in zero page) allows you to use instructions like LDA (pointeraddress),Y or LDA (pointeraddress,X). When X = Y = 0 the accumulator would be loaded with the contents of the memory the pointer points to. Next you can sent this char to your terminal. Then you need to increment the pointer (inc pointer / bne skip / inc pointer+1 / skip: next_instruction). And all this loops until your accu is loaded with 0 - your string termination char.

It's a bit bulky, but those were the days :D

If you know that allways ever your strings are <=255 char in length you may use LDA (pointer),Y , starting with Y = 0 and then INY within the loop leaving the pointer unchanged. This is a bit shorter.


Top
 Profile  
Reply with quote  
PostPosted: Sun Apr 09, 2017 2:54 am 
Offline
User avatar

Joined: Tue Mar 05, 2013 4:31 am
Posts: 1385
Dan Moos wrote:
I have another programing question.

It involves indirect addressing and strings.

Lets say I have some pre-canned messages I've placed in ROM.

Lets say I have:

Code:
error_message:     .BYTE   "Why would you type such a foolish thing", $00


Also lets say I have a subroutine who's job is to output strings. So I'd have some memory location where I'd stash the address of the desired string (my error message in this case) before jmp'ing to the subroutine. Basically I need a pointer.

My books allude to the indexed address modes being the way to do pointers, but as far as I can tell, you are stuck using 0 page as the location for your pointers. Not that that's a problem I guess. I'd only need 2 bytes for my whole messaging system.

How is this typically done?


I have a fair number of messages in my monitor. I use single byte value for a message number and have a two-byte pointer that holds the starting address of the text message. All text messages are terminated with a null character. To send a message to the console, I load the accumulator with the message number and JSR to the prompt routine:

Code:
;PROMPT routine: Send indexed text string to terminal. Index is A reg
;string buffer address is stored in variable PROMPTL, PROMPTH
;Routine is placed here in the Commands area to save ROM space
PROMPT      ASL   A   ;Multiply by two for msg table index
               TAY   ;Xfer to index
               LDA   MSG_TABLE,Y   ;Get low byte address
               STA   PROMPTL   ;Store in Buffer pointer
               LDA   MSG_TABLE+1,Y   ;Get high byte address
               STA   PROMPTH   ;Store in Buffer pointer
;               
PROMPT2      LDA   (PROMPTL)   ;Get string data
               BEQ   NOCHAR   ;If null character, exit (borrowed RTS)
               JSR   CHROUT   ;Send character to terminal
               INC   PROMPTL   ;Increment low byte index
               BNE   PROMPT2   ;Loop back for next character
               INC   PROMPTH   ;Increment high byte index
               BRA   PROMPT2   ;Loop back and continue printing


I also use a lot 65C02 specific opcodes and addressing modes.

_________________
Regards, KM
https://github.com/floobydust


Top
 Profile  
Reply with quote  
PostPosted: Sun Apr 09, 2017 2:57 am 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8543
Location: Southern California
Writing up a cross-reference of the assembler directives (including the macro language!) in all the assemblers would indeed be a huge job, that is, for researching them all. I'm sure even the most experienced 6502'ers have used less than five assemblers for the '02. I've used three, one of them being my own which I wrote to use in my Forth system, plus C32, and 2500AD.

You don't need ZP variables to access strings. You can use absolute,X. You'll need ZP if you do (ZP,X) or (ZP),Y addressing. Without the indirects, indexing is available anywhere, not just in ZP.

Suppose your error message is needed is several places, and it makes sense to only have the data once, instead of repeating it everywhere it's needed. So you might do for example,
Code:
          LDA   #<ErrMsg1     ; Get the low byte of the message address (but give it a descriptive name)
          LDY   #>ErrMsg1     ; and the high byte.
          JSR   DispMsg       ; The subroutine will put them in a variable, and index it.
          <continue>

          <and somewhere else in memory, probably with a collection of other strings:>
ErrMsg1:  BYTE  "Why would you type such a foolish thing?", 0

If the data are only needed in one place, another way to do it is to have the data immediately follow the JSR. It won't need a label. The subroutine uses the return address that the JSR puts on the stack to find the data, and it also adjusts that address so when the RTS is reached, the program counter goes to the first instruction after the data, rather than trying to execute data as if it were instructions. I address this, with example code, in the "Inlining subroutine data" chapter of the stacks treatise.

You will soon want to get into macros so you can do the same thing with the same output while making your source code shorter and more clear. (This is covered at the link above too.)

I see floobydust posted while I was writing. I like his table method too.

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


Top
 Profile  
Reply with quote  
PostPosted: Sun Apr 09, 2017 2:59 am 
Offline

Joined: Sat Mar 11, 2017 1:56 am
Posts: 276
Location: Lynden, WA
Ok, that's kinda what I figured. I had wondered if the was fine other trick that I want privy to.

What's the best way to put my address in the pointer? Basically, say I have my pointer at $0000, and my message at some address in ROM. Do I have to put the high and low bytes of the address in one at a time?

I think that is the thing I'm most stuck on. How do I take a 16 bit address, and place that address in my pointer location?


Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 321 posts ]  Go to page Previous  1 ... 3, 4, 5, 6, 7, 8, 9 ... 22  Next

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: