6502.org Forum  Projects  Code  Documents  Tools  Forum
It is currently Sun Nov 24, 2024 8:44 pm

All times are UTC




Post new topic Reply to topic  [ 8 posts ] 
Author Message
PostPosted: Sun Dec 04, 2016 2:50 am 
Offline

Joined: Tue Jun 08, 2004 11:51 pm
Posts: 213
Lessons I've, just, learned the hard way. I thought I'd share
them.
Moving or copying blocks of memory can be tricky with the
6502. I've made all the mistakes so I thought, I'd put them
here to, maybe, save others the pain.

A simple move, down in memory, might look like:
Code:
   ; ON PAGE ZERO RESERVE SOME SPACE

IXFRM   DW 0      ; INDEX FROM
   IXFRMH=INDXF+1
IXTO   DW 0      ; INDEX TO


   ; sOME PLACE ELSE
   SIZE = $55
   FROM = $100
   TO = $210

MOV1    LDX # (FROM-1)&$0FF           ; depending on the assembler you might
        STX IXFRM                 ;  not need any '&$0FF' but I include it
        LDX # ((FROM-1)/$100)&$0FF    ;  here for completeness
        STX IXFRMH
        LDX # (TO-1)&$0FF
        STX IXTO
        LDX # ((TO-1)/$100)&#0FF
        LDY # SIZE
MOV11   LDA(IXFRM),Y
        STA(IXTO),Y
        DEY
        BNE MOV11

All looks OK but what say you wanted to move $100.
if you do it the same, you can't set Y=$100 for the
size.
An alturnative might look like:
Code:
MOV2   LDX # FROM&$0FF
        STX IXFRM
        LDX # (FROM/$100)&$0FF
        STX IXFRMH
        LDX # TO&$0FF
        STX IXTO
        LDX # (TO/$100)&0FF
        LDY # SIZE
MOV21   LDA(IXFRM),Y
        STA(IXTO),Y
        DEY
        BNE MOV21

Of course, anything over $100 would need to be done
in multiple chunks of $100 or less.
There is a potential problem with MOV2. If the from
data overlaps the first byte of the to location, the
first byte of the to location is over written.
If one is making a general case of a move down, it is
safer to use blocks smaller than $100 in size. The
math is simplest for $80 in size and use MOV1 and
avoid MOV1.
An alternative that, take more, code might be:
Code:
MOV3   LDX # FROM&$0FF
        STX IXFRM
        LDX # (FROM/$100)&$0FF
        STX IXFRMH
        LDX # TO&$0FF
        STX IXTO
        LDX # (TO/$100)&0FF
        LDY # SIZE
MOV31   DEY
        BNE MOV22
    LDA(IXFRM),Y
        STA(IXTO),Y
   JMP MOV21
MOV32

It still won't do a size of $100.

Just now learning my 6502 coding.
Dwight


Last edited by dwight on Sun Dec 04, 2016 4:02 pm, edited 1 time in total.

Top
 Profile  
Reply with quote  
PostPosted: Sun Dec 04, 2016 4:04 am 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8546
Location: Southern California
Dwight, to make your code appear as intended, monospaced and without the spaces removed, put [code] and [/code] around the source-code sections.

Bruce Clark has a short article on this site about memory-moving, at http://6502.org/source/general/memory_move.html . One thing I don't see there is self-modifying code which could make it a little faster and more memory-efficient. This is where the variables are the operands of instructions themselves. If your routines are in RAM, you might want to experiment with that.

Note that the 65816 has the memory-moving instructions MVN and MVP. You set up X, Y, and C (16-bit A) to hold the source, destination, and count, then do MVN or MVP with the operands being the source and destination bank numbers you want. Up to 64K can be moved at once, at seven cycles per byte, and it's interruptible.

_________________
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 Dec 04, 2016 4:36 am 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8546
Location: Southern California
I find I seldom have to move 256 bytes or more, since most moves are strings or other short pieces, so in my '02 Forth, I added QCMOVE and QCMOVE> (adding the "quick" to the standard "character move" and "character move up" words). Here's an approximate translation of them. FROM and TO and COUNT are ZP variables.

Code:
QCMOVE_UP:
        LDY  COUNT
        BEGIN
           DEY
           CPY  #$FF
        WHILE_NEQ
           LDA  (FROM),Y
           STA  (TO),Y
        REPEAT
        RTS
 ;--------------

QCMOVE: LDY  #0
        BEGIN
           CPY  COUNT
        WHILE_NEQ
           LDA  (FROM),Y
           STA  (TO),Y
           INY
        REPEAT
        RTS
 ;--------------

The structure macros are from my article on the subject; but I have no doubt that you can guess exactly what they assemble.

_________________
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 Dec 04, 2016 5:46 am 
Offline

Joined: Tue Jun 08, 2004 11:51 pm
Posts: 213
I'd forgot about the html code makers.
Thanks for reminding me.
Could a moderator move this to programming, I seem to have
put it in the wrong place.
I had a number of different sized blocks to move that were from
my test EPROM code, if you see my KIM-1 stuff in nostalgia.
one block more than $100. It seemed like before I thought about
it, I'd fix one section and break the other, back and forth.
When I thought about it I saw what I was doing.
I'd played with multiple block moves in RAM doing self modifying code.
I kept the FROM and TO pointer separate so after each block,
I'd do two increments for each pointer. That allowed me to have one
subroutine to do all the moving. I was trying to move every
thing into a single EPROM but finally gave up. I just didn't have enough
free space. It was for micochess. If I didn't load the one opening moves,
I could have done it but not with it. I only have the 1K window to work
with and a few extra bytes.
Bruce doesn't mention about overlapping memory for moves.
Dwight


Top
 Profile  
Reply with quote  
PostPosted: Sun Dec 04, 2016 7:44 am 
Offline
User avatar

Joined: Thu May 28, 2009 9:46 pm
Posts: 8514
Location: Midwestern USA
GARTHWILSON wrote:
Note that the 65816 has the memory-moving instructions MVN and MVP...Up to 64K can be moved at once, at seven cycles per byte, and it's interruptible.

MVN can also be used as a high-speed block fill method.

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


Top
 Profile  
Reply with quote  
PostPosted: Sun Dec 04, 2016 3:53 pm 
Offline

Joined: Tue Jun 08, 2004 11:51 pm
Posts: 213
[quote="GARTHWILSON"]I find I seldom have to move 256 bytes or more, since most moves are strings or other short pieces, so in my '02 Forth, I added QCMOVE and QCMOVE> (adding the "quick" to the standard "character move" and "character move up" words).

Hi Garth
I should note that I don't use a regular assembler. I have fpc running on an older 486 that I use for blowing EPROMs. It has a PB-10 (ISA bus). I have fpc running on it and use a single pass assembler I wrote in Forth. It has no infixed notation. I use macros to eliminate meaningless labels as you do.
I deal with large forward references by what I call Segments. I define a segment like this:
Code:
  0C00 segment Main-Code
  FFFA segment Int-Vectors

Main-Code
  ... some code ...
label NMI-Code
  ... some code ...

Int-Vectors
  NMI-Code A,

Main-Code
  ... more main code ....

Each segment word keeps track of where it was so that invoking its name moves
the assembly pointer back to the last location to be used by that segment.
It does this by indirecting the assembly pointer.
I don't do any intentional warning for overlapping code but I do manually check
before writing it all out to files.
Using macro structures for code, like yours, such as:
Code:
begin
  From lda(),y
  To     sta(),y
   dey
zero until

similar to what you have.
Dwight


Top
 Profile  
Reply with quote  
PostPosted: Sun Dec 04, 2016 4:31 pm 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8546
Location: Southern California
dwight wrote:
Hi Garth
I should note that I don't use a regular assembler. I have fpc running on an older 486 that I use for blowing EPROMs. It has a PB-10 (ISA bus).

That's a piece of gold! Take care of that and don't let it get away! (I have a PB-10 too, in a similar old PC.) I seldom use it, but I hope that it (especially the PB10) never quits working. The assembler I use on the PC is Cross-32 (C32), originally from Universal Cross Assemblers. I think I still have 2500AD's assembler too, but haven't used it in decades.

Quote:
I have fpc running on it and use a single pass assembler I wrote in Forth. It has no infixed notation.

The assembler I wrote that runs in the Forth on my workbench computer does not do any parsing, but it still puts the mnemonic before the operand, so you have for example
Code:
        LDY_ZP  COUNT  C,
where you comma-in the operand. Being Forth, you can do any desired amount of calculation to figure out the operand before comma'ing it in. The LDY_ZP only lays down the op code. Branches have slightly more complexity, but still start with laying down the op code, like BNE. In its original form, I wrote it in one evening. It was an exciting moment when I realized macro capability was automatic.

Quote:
I use macros to eliminate meaningless labels as you do.
I deal with large forward references by what I call Segments. I define a segment like this:

That's something I have not seen before.

_________________
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: Mon Dec 05, 2016 1:12 am 
Offline

Joined: Tue Jun 08, 2004 11:51 pm
Posts: 213
GARTHWILSON wrote:
dwight wrote:
Hi Garth
I deal with large forward references by what I call Segments. I define a segment like this:

That's something I have not seen before.


Here is the code:
Code:

variable SegPntr

: AHere ( - Address )  \ used by some of the assembly words to assemble
   SegPntr @ @ ;

: Segment ( AddressStart - | Name )
   create ,
   does> SegPntr ! ;

: AC, ( byte - )            \ assemble a byte
  >r SegPntr @ dup @ \ get all the pointers
  r> over CodeBuf + c! \ put the byte into the buffer
  1+ dup swap ! ;         \ Update the assembly address



When I look at the code now, I realize it would have been
better to update the current pointer when changing segments rather
then updating for each byte but it works as it is. It is just not as
efficient as it could be. I wrote this assembler at least 20-25 years ago.
Although, not in this assembler, for other assemblers, I have
a way of creating a linked list for forward references that patch
themselves when the reference is resolved. I uses a lot of space
for the linked list but allows a different method of keeping things
as a single pass.
One has to predefine a template similar to what is done in C.
Since I always assemble in a separate buffer that one can use
as a virtual space of any size, I can arbitrarily build the linked
list in Forth's definition space.
One has to be careful if doing forgets as these links are not attached
tightly to any word. Things are so fast at compiling now that I
recompile the entire assembler and the code at the same time.
Any changes, I just delete all the definitions.
It is done before I have time to remove my finger from the enter
key.
It only takes another millisecond to write the finished image code to
disk.
I still sometimes do assembly on my IMSAI under CP/M.
It is astounding how far we've come.
Dwight


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

All times are UTC


Who is online

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