Dan's 6502 build, aka, The WOPR Jr.
Re: Dan's 6502 build, aka, The WOPR Jr.
GBZM, I fully get that. I think you missed the gist of my question. Why do I need to st side RAM, when no one is accessing it but me?
Garth mostly cleared it up, if I'm understanding him right. Basically, it's go let the assembler do the hard work of arranging RAM usage in an organized way.
Garth mostly cleared it up, if I'm understanding him right. Basically, it's go let the assembler do the hard work of arranging RAM usage in an organized way.
- BigDumbDinosaur
- Posts: 9428
- Joined: 28 May 2009
- Location: Midwestern USA (JB Pritzker’s dystopia)
- Contact:
Re: Dan's 6502 build, aka, The WOPR Jr.
Dan Moos wrote:
ok, still a little hung up on the .DS directive and its various brethren.
Does a directive like that have any use if your program is going in ROM?
Does a directive like that have any use if your program is going in ROM?
Quote:
I guess what still confuses me is, with my code in ROM, why would I even have to set aside any block of memory in RAM, if nothing goes anywhere in RAM that I don't specifically designate. So if I want $0200 to $02FF to be4 a buffer for instance, I could just not ever use it for anything else.
Quote:
Also, I had read BDD's exposition of the Kowalski directives and operators. I didn't ses mention of the use of the "<" and ">" operators being used that way.
x86? We ain't got no x86. We don't NEED no stinking x86!
- barrym95838
- Posts: 2056
- Joined: 30 Jun 2013
- Location: Sacramento, CA, USA
Re: Dan's 6502 build, aka, The WOPR Jr.
Here's a simple but non-trivial example:
http://rosettacode.org/wiki/FizzBuzz/As ... 2_Assembly
The top three lines are housekeeping.
The Constant section contains values that don't need storage and don't change after assembly, but are named for the purpose of readability.
The String Literal section doesn't change after assembly, but does need storage allocated, so it is given a label so the assembler can place it directly after the code section. If the code section ever expands or contracts, the address of the String Literal section will change, but the assembler will automatically handle the change and modify the operands accordingly, just like it would for a branch target that moved due to a revision or addition.
The Variable section contains cells that do change during execution (the values, not the addresses), and once again the assembler is trusted to allocate space for them and keep track of their addresses as operands for the instructions using them, no matter how many times the code section may shrink or expand during subsequent revisions.
Mike B.
P.S. This is also an example of how using TABs instead of SPACEs can turn a tidy looking source into a mess, depending on where you post it.
http://rosettacode.org/wiki/FizzBuzz/As ... 2_Assembly
Code: Select all
.lf fzbz6502.lst
.cr 6502
.tf fzbz6502.obj,ap1
;------------------------------------------------------
; FizzBuzz for the 6502 by barrym95838 2013.04.04
; Thanks to sbprojects.com for a very nice assembler!
; The target for this assembly is an Apple II with
; mixed-case output capabilities and Applesoft
; BASIC in ROM (or language card)
; Tested and verified on AppleWin 1.20.0.0
;------------------------------------------------------
; Constant Section
;
FizzCt = 3 ;Fizz Counter (must be < 255)
BuzzCt = 5 ;Buzz Counter (must be < 255)
Lower = 1 ;Loop start value (must be 1)
Upper = 100 ;Loop end value (must be < 255)
CharOut = $fded ;Specific to the Apple II
IntOut = $ed24 ;Specific to ROM Applesoft
;======================================================
.or $0f00
;------------------------------------------------------
; The main program
;
main ldx #Lower ;init LoopCt
lda #FizzCt
sta Fizz ;init FizzCt
lda #BuzzCt
sta Buzz ;init BuzzCt
next ldy #0 ;reset string pointer (y)
dec Fizz ;LoopCt mod FizzCt == 0?
bne noFizz ; yes:
lda #FizzCt
sta Fizz ; restore FizzCt
ldy #sFizz-str ; point y to "Fizz"
jsr puts ; output "Fizz"
noFizz dec Buzz ;LoopCt mod BuzzCt == 0?
bne noBuzz ; yes:
lda #BuzzCt
sta Buzz ; restore BuzzCt
ldy #sBuzz-str ; point y to "Buzz"
jsr puts ; output "Buzz"
noBuzz dey ;any output yet this cycle?
bpl noInt ; no:
txa ; save LoopCt
pha
lda #0 ; set up regs for IntOut
jsr IntOut ; output itoa(LoopCt)
pla
tax ; restore LoopCt
noInt ldy #sNL-str
jsr puts ;output "\n"
inx ;increment LoopCt
cpx #Upper+1 ;LoopCt >= Upper+1?
bcc next ; no: loop back
rts ; yes: end main
;------------------------------------------------------
; Output zero-terminated string @ (str+y)
; (Entry point is puts, not outch)
;
outch jsr CharOut ;output string char
iny ;advance string ptr
puts lda str,y ;get a string char
bne outch ;output and loop if non-zero
rts ;return
;------------------------------------------------------
; String literals (in '+128' ascii, Apple II style)
;
str: ; string base offset
sFizz .az -"Fizz"
sBuzz .az -"Buzz"
sNL .az -#13
;------------------------------------------------------
; Variable Section
;
Fizz .da #0
Buzz .da #0
;------------------------------------------------------
.en
The Constant section contains values that don't need storage and don't change after assembly, but are named for the purpose of readability.
The String Literal section doesn't change after assembly, but does need storage allocated, so it is given a label so the assembler can place it directly after the code section. If the code section ever expands or contracts, the address of the String Literal section will change, but the assembler will automatically handle the change and modify the operands accordingly, just like it would for a branch target that moved due to a revision or addition.
The Variable section contains cells that do change during execution (the values, not the addresses), and once again the assembler is trusted to allocate space for them and keep track of their addresses as operands for the instructions using them, no matter how many times the code section may shrink or expand during subsequent revisions.
Mike B.
P.S. This is also an example of how using TABs instead of SPACEs can turn a tidy looking source into a mess, depending on where you post it.
Last edited by barrym95838 on Mon Apr 10, 2017 1:03 am, edited 2 times in total.
Re: Dan's 6502 build, aka, The WOPR Jr.
your example of
actually points out my confusion. Doing it THAT way makes perfect sense to me. Is that completely equivalent to a .DS statement?
I wish I were better at wording my questions. Seems to be a lot of confusion over what I am asking.
Code: Select all
BUFFER = $0200
BUFFEND = $02FFI wish I were better at wording my questions. Seems to be a lot of confusion over what I am asking.
Re: Dan's 6502 build, aka, The WOPR Jr.
Ok, in the directive list you posted, we have
I know what *= means. What does *=* mean?
Code: Select all
.DS .RS ;equivalent to *=*+N, where N is arg to .DS- GARTHWILSON
- Forum Moderator
- Posts: 8775
- Joined: 30 Aug 2002
- Location: Southern California
- Contact:
Re: Dan's 6502 build, aka, The WOPR Jr.
There's nothing wrong with doing something like the BUFEND = $02FF; but if there's ever any question about how big the buffer is, I would instead tend to have a constant telling the size of the buffer, rather than a label to the last address of the buffer. Then if you ever need to move the buffer, you only have to change the one label, not both.
My original code from the interrupts primer (which I think you started with) assumes the full 256 bytes for the buffer, regardless of where it starts (as long as it's all in RAM). I did it that way partly because I do have a use for that much, and partly because it makes the routines shorter and faster if you don't have to make the pointers wrap at something else like 128 or 64, let alone numbers like 75 or 100 which take more than just ANDing-out a bit or two in binary.
The *=* is part of *="+N.
* in this case is the program counter.
*+N means "Take the program counter's current value and add N to it."
= is the assignment operator; so
the whole things means "Take the program counter, add N to it, and put the result of that addition back in the program counter. (It's like A=A+B in BASIC.)
My original code from the interrupts primer (which I think you started with) assumes the full 256 bytes for the buffer, regardless of where it starts (as long as it's all in RAM). I did it that way partly because I do have a use for that much, and partly because it makes the routines shorter and faster if you don't have to make the pointers wrap at something else like 128 or 64, let alone numbers like 75 or 100 which take more than just ANDing-out a bit or two in binary.
The *=* is part of *="+N.
* in this case is the program counter.
*+N means "Take the program counter's current value and add N to it."
= is the assignment operator; so
the whole things means "Take the program counter, add N to it, and put the result of that addition back in the program counter. (It's like A=A+B in BASIC.)
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?
The "second front page" is http://wilsonminesco.com/links.html .
What's an additional VIA among friends, anyhow?
Re: Dan's 6502 build, aka, The WOPR Jr.
Dan Moos wrote:
GBZM, I fully get that. I think you missed the gist of my question. Why do I need to st side RAM, when no one is accessing it but me?
Garth mostly cleared it up, if I'm understanding him right. Basically, it's go let the assembler do the hard work of arranging RAM usage in an organized way.
Garth mostly cleared it up, if I'm understanding him right. Basically, it's go let the assembler do the hard work of arranging RAM usage in an organized way.
sorry. I don't want to offend you. Just one counterquestion:
How should the assembler you have chosen know about your hardware, how should it know where is ROM,RAM,I/O or perhaps nothing?
I haven't heard about an assembler having a configuration and options settings section like C compilers for µCs have. These settings are part of the asm source.
- BigDumbDinosaur
- Posts: 9428
- Joined: 28 May 2009
- Location: Midwestern USA (JB Pritzker’s dystopia)
- Contact:
Re: Dan's 6502 build, aka, The WOPR Jr.
Dan Moos wrote:
your example of
actually points out my confusion. Doing it THAT way makes perfect sense to me. Is that completely equivalent to a .DS statement?
I wish I were better at wording my questions. Seems to be a lot of confusion over what I am asking.
Code: Select all
BUFFER = $0200
BUFFEND = $02FFI wish I were better at wording my questions. Seems to be a lot of confusion over what I am asking.
Suppose you have written a keyboard input routine for your program. As the user types you have to collect keystrokes somewhere and when the user types [CR] (symbol for the return key) to "enter" his or her data, you have mark the end of the series of collected keystrokes and then go on to process the input. You collect the keystrokes in a buffer that must be large enough to hold the maximum number of keystrokes you deem to be adequate, as well as one extra space to mark the end of the string of keystrokes. Conventionally, a null ($00) is used as an end-of-string (EOS) marker. You can use whatever you want, or could even have a separate index that tells your program the offset to EOS in the buffer. You're the programmer, so it's your call.
In order to define such a buffer, you could take the following steps:
- Define the maximum number of keystrokes you are willing to accept. In assembly language, that definition would be encoded as maxkeys = <some_number>. For example, maxkeys = 60 means just what it says.
- Using the maxkeys definition, you would then define your buffer size. If you use a terminator to mark the EOS of the user's input the buffer must be one byte larger than maxkeys. So you would write bufsiz = maxkeys+1. Otherwise, bufsiz would equal maxkeys.
- Later on in your program, where you would be declaring dynamic storage, you would define the buffer itself as follows:
The above would set inputbuf to the current value of the program counter and then advance the latter by bufsiz bytes. You have now reserved 61 bytes for your input buffer. Elsewhere in your program, you can read and write your buffer by using its name, not some arbitrary address.Code: Select all
inputbuf *=*+bufsiz
Here's the above sequence recapitulated:
Code: Select all
maxkeys = 60 ;maximum allowed keyboard input
bufsiz = maxkeys+1 ;buffer size w/terminator
---
* = $0200 ;set lcation to $0200 (decimal 512)
;
inputbuf *=*+bufsiz ;define input buffer...
;
; ———————————————————————————————————————————————————————————
; The above statement sets the starting location of the input
; buffer to $0200 and reserves 61 bytes. The next storage
; definition will be at $023D.
; ———————————————————————————————————————————————————————————
;
flag *=*+1 ;this flag will be at $023Dx86? We ain't got no x86. We don't NEED no stinking x86!
Re: Dan's 6502 build, aka, The WOPR Jr.
I realize the assembler is ignorant of the hardware outside the CPU. I understand I have to give it very specific addresses for everything. Like I said, I think you missed what I was asking.
I didn't see why I had to specifically set aside that memory, when I could just not use it for anything else. Since I'm the only one assigning memory, at the time it seemed an unnecessary step. At the time I figured, why couldn't I just label the start of the buffer, and then never assign any memory in the range "buffer + size of buffer" for non buffer use.
What I think I've learned since, is that yeah, I could do it that way, but it's way easier to let the assembler do the hard work, and more importantly, since I'm working with a size, and not specific addresses, I can change the size of the buffer without a cascade of addresses I've foolishly hard-coded becoming invalid.
Ok, that explanation of the "*" operator was what I needed. I didn't realize that "*" actually was the program counter. I thought "*=" was a single directive. Now that I know that "*" is a stand alone symbol, "*=*+N" makes perfect sense.
If that nugget is in any of the 4 books I'm working with, (including the oft cited Lichty and Eyes), I've sure missed it. You see stuff like that used, but not explained.
Like a said before, learning 6502 assembly has been ok for me so far, but I still seems like a massive research effort to find novice level explanations of assembler directives. Just as soon as I think I have a handle, someone posts a code snippet that has yet more directives that are not in common with my assembler. I'll get it, it's just frustrating because no good one stop info source seems to exist for directives.
How did things get to where there was no standard? I guess I know how these things happen, but sheesh!
I've been trying to download the WDC programming manual from their site, but it's a dead link. Anyone know where else to find it?
I didn't see why I had to specifically set aside that memory, when I could just not use it for anything else. Since I'm the only one assigning memory, at the time it seemed an unnecessary step. At the time I figured, why couldn't I just label the start of the buffer, and then never assign any memory in the range "buffer + size of buffer" for non buffer use.
What I think I've learned since, is that yeah, I could do it that way, but it's way easier to let the assembler do the hard work, and more importantly, since I'm working with a size, and not specific addresses, I can change the size of the buffer without a cascade of addresses I've foolishly hard-coded becoming invalid.
Ok, that explanation of the "*" operator was what I needed. I didn't realize that "*" actually was the program counter. I thought "*=" was a single directive. Now that I know that "*" is a stand alone symbol, "*=*+N" makes perfect sense.
If that nugget is in any of the 4 books I'm working with, (including the oft cited Lichty and Eyes), I've sure missed it. You see stuff like that used, but not explained.
Like a said before, learning 6502 assembly has been ok for me so far, but I still seems like a massive research effort to find novice level explanations of assembler directives. Just as soon as I think I have a handle, someone posts a code snippet that has yet more directives that are not in common with my assembler. I'll get it, it's just frustrating because no good one stop info source seems to exist for directives.
How did things get to where there was no standard? I guess I know how these things happen, but sheesh!
I've been trying to download the WDC programming manual from their site, but it's a dead link. Anyone know where else to find it?
Re: Dan's 6502 build, aka, The WOPR Jr.
I crossed posts with you BDD, so sorry if it sounds like I didn't read it. 
- BigDumbDinosaur
- Posts: 9428
- Joined: 28 May 2009
- Location: Midwestern USA (JB Pritzker’s dystopia)
- Contact:
Re: Dan's 6502 build, aka, The WOPR Jr.
Dan Moos wrote:
Ok, that explanation of the "*" operator was what I needed. I didn't realize that "*" actually was the program counter. I thought "*=" was a single directive. Now that I know that "*" is a stand alone symbol, "*=*+N" makes perfect sense.
Quote:
Like a said before, learning 6502 assembly has been ok for me so far, but I still seems like a massive research effort to find novice level explanations of assembler directives. Just as soon as I think I have a handle, someone posts a code snippet that has yet more directives that are not in common with my assembler. I'll get it, it's just frustrating because no good one stop info source seems to exist for directives.
Quote:
How did things get to where there was no standard? I guess I know how these things happen, but sheesh!
x86? We ain't got no x86. We don't NEED no stinking x86!
Re: Dan's 6502 build, aka, The WOPR Jr.
yup. once I realized "*" specifically represented the counter, all made sense.
- GARTHWILSON
- Forum Moderator
- Posts: 8775
- Joined: 30 Aug 2002
- Location: Southern California
- Contact:
Re: Dan's 6502 build, aka, The WOPR Jr.
Dan Moos wrote:
Ok, that explanation of the "*" operator was what I needed. I didn't realize that "*" actually was the program counter. I thought "*=" was a single directive. Now that I know that "*" is a stand alone symbol, "*=*+N" makes perfect sense.
[...] still seems like a massive research effort to find novice level explanations of assembler directives. Just as soon as I think I have a handle, someone posts a code snippet that has yet more directives that are not in common with my assembler. I'll get it, it's just frustrating because no good one stop info source seems to exist for directives.
[...] still seems like a massive research effort to find novice level explanations of assembler directives. Just as soon as I think I have a handle, someone posts a code snippet that has yet more directives that are not in common with my assembler. I'll get it, it's just frustrating because no good one stop info source seems to exist for directives.
Code: Select all
ORG $ + NAlthough the following is in the interrupts primer, including with an illustration, I should comment again that the RS-232 buffer as I'm using it there is a ring, a circle. There is effectively no beginning or end. You don't have to parse an input string all the way to the end before allowing a new string to start being accepted. It's kind of like a gas tank which does not need to be emptied before you put more gas in. (There's probably a better analogy to be had; but you probably get the idea.) Regardless of what's in it, if it's not full, you can add more. Just make sure you tell the sender to stop sending when the buffer is nearly full, so you don't overflow it, ie, you don't overwrite the oldest material in it that's still waiting to be handled.
Also, there's no requirement that the incoming data all be in strings, whether counted or null-terminated. It might for example start with a string to tell it that the next 913 bytes are binary program material to put in memory starting at address xyz. After that many bytes have come in, the next bytes might again be a string giving the computer the next instruction.
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?
The "second front page" is http://wilsonminesco.com/links.html .
What's an additional VIA among friends, anyhow?
- BigDumbDinosaur
- Posts: 9428
- Joined: 28 May 2009
- Location: Midwestern USA (JB Pritzker’s dystopia)
- Contact:
Buffers, FIFOs & LIFOs
GARTHWILSON wrote:
Ok, just to add another one, hopefully ahead of time so it won't mess you up further when you see it: with some assemblers, $ by itself (not followed by hex digits) is the way to get the value of the program counter. So you might see something like
which is equivalent to the *=*+N that we had above. Yeah, there's definitely a lack of consistency.
Code: Select all
ORG $ + NQuote:
Although the following is in the interrupts primer, including with an illustration, I should comment again that the RS-232 buffer as I'm using it there is a ring, a circle...It's kind of like a gas tank which does not need to be emptied before you put more gas in...
In assembly language, it's helpful to think of the structure to which Garth is referring as a FIFO (first-in, first-out), not a buffer. In academic terms, a buffer is a temporary storage structure in which the structure's logical beginning and end are the same as its physical beginning and end. You would use a buffer for collecting typed input, storing a disk block, etc.
On the other hand, a FIFO's logical beginning and end is constantly moving—hence the FIFO is "circular." An index is maintained somewhere to tell the program using the FIFO where the logical beginning is located within the FIFO's boundaries. The same index implies the location of the logical end, which is one less than the beginning. Typically that index is zero-based and is added to the starting address of the FIFO to get the actual address of the logical beginning. As you develop your TIA-232 driver routines you will get very up close and personal with FIFOs.
A stack, whether hardware or software, is a LIFO (last-in, first-out) structure and as is the case with the FIFO, the logical beginning and end constantly moves—making a FIFO "circular." Considering a hardware stack as in the 6502 family, each push to the stack decrements the stack pointer (SP) after the byte has been written to the stack (SP is postdecremented). If a push occurs while SP is $00 the pushed byte will be stored at $0100 (in the eight bit MPUs) and SP will wrap to $FF. Pulling a byte will increment SP before the byte is read from the stack and if SP is $FF when a byte is pulled SP will wrap to $00. Software stacks work in essentially the same fashion, but the "stack pointer" is (sometimes laboriously) maintained in software.
Quote:
Also, there's no requirement that the incoming data all be in strings, whether counted or null-terminated. It might for example start with a string to tell it that the next 913 bytes are binary program material to put in memory starting at address xyz. After that many bytes have come in, the next bytes might again be a string giving the computer the next instruction.
x86? We ain't got no x86. We don't NEED no stinking x86!
- GARTHWILSON
- Forum Moderator
- Posts: 8775
- Joined: 30 Aug 2002
- Location: Southern California
- Contact:
Re: Buffers, FIFOs & LIFOs
BigDumbDinosaur wrote:
GARTHWILSON wrote:
Ok, just to add another one, hopefully ahead of time so it won't mess you up further when you see it: with some assemblers, $ by itself (not followed by hex digits) is the way to get the value of the program counter. So you might see something like
which is equivalent to the *=*+N that we had above. Yeah, there's definitely a lack of consistency.
Code: Select all
ORG $ + N- Cross-32 (C32) originally from Universal Cross-Assemblers. I've used it for 65c02 and '816, but it's also good for lots of other processors. I bought it in the mid-1990's for $99.
- the 2500AD assembler which I've used for 65c02. My employer paid $250 for in about 1987.
- Microchip's MPASM assembler for PIC microcontrollers.
Quote:
In assembly language, it's helpful to think of the structure to which Garth is referring as a FIFO (first-in, first-out), not a buffer. [...]
Quote:
Quote:
Also, there's no requirement that the incoming data all be in strings, whether counted or null-terminated. It might for example start with a string to tell it that the next 913 bytes are binary program material to put in memory starting at address xyz. After that many bytes have come in, the next bytes might again be a string giving the computer the next instruction.
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?
The "second front page" is http://wilsonminesco.com/links.html .
What's an additional VIA among friends, anyhow?