texacala Posted August 21, 2011 Share Posted August 21, 2011 Ok, me again with another assembly problem to solve: clearing out a large block of RAM. 6502.org has a short little routine for clearing up to 256 bytes, but what about clearing out 8K? Let's say I want to store zeros in memory locations 32000 to 40000. The X and Y registers only hold one byte each, so I can't very well INX 8000 times. What about nested loops? I could INX up to 255, store a running total, then INY, and perform the X loop again. I can use two bytes to store the running total. Am I on the right track? I haven't been able to find a routine that does this. Quote Link to comment Share on other sites More sharing options...
MaPa Posted August 21, 2011 Share Posted August 21, 2011 Depends what you exactly need. If clearing by pages is enough, you can use self-modifying code like this for example. lda #0 ldx #0 ldy #<number of pages to clear> loop sta <address>,x inx bne loop inc loop+2 ; increasing HI-byte of the clearing address dey bne loop Quote Link to comment Share on other sites More sharing options...
+Stephen Posted August 21, 2011 Share Posted August 21, 2011 Very cool code MaPa. I have not used self modifying code yet. Tex - please take a look at my blog post here for a nice short example that can be called from BASIC via USR call. It is relocatable, and takes 2 parameters - starting address (must be page aligned) and # of pages to clear. Blog has more details - I'll repost the code here as well. ;Original create date 01/06/10 - This is assembled to "MEMCLEAR.OBJ" ;This will be called from a USR function ;1st parameter is # of parms (1 byte) ;2nd parameter is STARTing page of mem to clear (2 bytes hi-lo) ;3rd parameter is # of pages to clear (2 bytes hi-lo) ;E.G. X=USR($600,2,3) ;STACK will contain ;02 - #paramaters ;02 - hi (address to clear - valid is 00 to FF) ;00 - lo (address to clear, will always want this to be 00) ;00 - hi (DISCARD) ;03 - lo (save in X as # pages to clear) ;BENCHMARK - according to a tracefile (A800Win+) clearing 3 pages of RAM ;takes 2326 instructions, 7749 cycles, .004 seconds ;Atari BASIC loop takes 4.76 seconds (acording to $13,$14) ;ASM version is 1,192 times faster! ;Equates MEMTOCLR equ $CE ;$CE and $CF will contain the address of RAM to clear org $0600 ;This code will be overwritten once used ;since calling from BASIC, clear stack and set up variables pla ;clear # parameters from stack pla sta MEMTOCLR+1 ;hi-byte pla sta MEMTOCLR ;lo-byte pla ;discard since we can't clear more than $FF pages pla tax ;# of pages to clear ;stack is clean, variables set, now do the work lda #0 ;so we can clear RAM ;# of pages to clear has been preloaded in X L2 ldy #0 ;will be our countdown timer L1 sta (MEMTOCLR),y ; dey bne L1 ;when we fall through to here we have cleared one page inc MEMTOCLR+1 ;prepare to clear the next page dex ;decrease page counter bne L2 ;if not zero, jump back and clear another page rts ;All done - return control to BASIC Stephen Quote Link to comment Share on other sites More sharing options...
Synthpopalooza Posted August 21, 2011 Share Posted August 21, 2011 If you were doing this in BASIC, you could assign a string to the memory location you wanted to clear, through the variable offset technique This article tells you how to set up the string offset: http://www.atarimagazines.com/compute/issue74/block_peek_poke.php Then, any op you do on the string affects the memory location. So to blank out, say, 1K of memory, you'd do this: 100 S$="[heart]": S$(1024)="[heart]":S$(2)=S$ Simple as that. Quote Link to comment Share on other sites More sharing options...
+Stephen Posted August 21, 2011 Share Posted August 21, 2011 If you were doing this in BASIC, you could assign a string to the memory location you wanted to clear, through the variable offset technique This article tells you how to set up the string offset: http://www.atarimagazines.com/compute/issue74/block_peek_poke.php Then, any op you do on the string affects the memory location. So to blank out, say, 1K of memory, you'd do this: 100 S$="[heart]": S$(1024)="[heart]":S$(2)=S$ Simple as that. Yes - that is a very cool tip. The reason I had this called from BASIC is I planned on doing some stuff in 100% assembler, but still need the "crutch" of BASIC for quickly prototyping the control flow, easy I/O, etc. So I was writing everything in BASIC and then taking the slowest pieces and converting to ASM. Quote Link to comment Share on other sites More sharing options...
texacala Posted August 21, 2011 Author Share Posted August 21, 2011 Yes MaPa, that is so elegant, just the thing I was looking for. Thank you. And thanks Stephen for another example-- I wasn't aware of your blog and I'll take a look! Quote Link to comment Share on other sites More sharing options...
Tyrop Posted August 21, 2011 Share Posted August 21, 2011 I am definitely an amateur assembly language programmer, but my 2cents is that your idea of a nested loop is a fine way to do it. MaPa's code is ingenious - it actually changes the actual program on the fly. Stephen's can be called from Basic. In the end, both examples still use a nested loop, and will loop through 8000 STA instructions for your application. You can also do the following, but I don't think it is any more efficient. Rather than nesting a loop, you can do one iteration of incrementing the X register from 0 to 255 and then have this: STA 32000,X STA 32000+256,X STA 32000+512,X STA 32000+768,X STA 32000+1024,X Etc... Until yo do all 8K. In this example, you are writing a lot more code, and still doing 8000 STA's, so I think nesting is still the better way to go. Quote Link to comment Share on other sites More sharing options...
texacala Posted August 21, 2011 Author Share Posted August 21, 2011 Oh one question-- Stephen in your routine you use +1 to address the HI byte of your MEMTOCLR. MaPa's code is using +2 to INC the HI byte of the clearing address. I would have guessed "+1" for both examples, I don't think I'm following why they are different. Quote Link to comment Share on other sites More sharing options...
MaPa Posted August 21, 2011 Share Posted August 21, 2011 (edited) My example is pretty basic piece of code IMHO, nothing extraordinary Oh one question-- Stephen in your routine you use +1 to address the HI byte of your MEMTOCLR. MaPa's code is using +2 to INC the HI byte of the clearing address. I would have guessed "+1" for both examples, I don't think I'm following why they are different. No, in my example +2 is right, in the other +1 is right. Because I increment memory location at label LOOP+2, there is sta <address>,x instruction which in memory looks like STA instruction code, LOW byte, HI byte. The other example is using indirect addressing and the label MEMTOCLR points to zero page location where is stored LOW byte, and +1 there is HI byte. I hope you understand. Edited August 21, 2011 by MaPa Quote Link to comment Share on other sites More sharing options...
Tyrop Posted August 21, 2011 Share Posted August 21, 2011 In MaPa's example, it is adding 1 to the hi byte. The expression, "loop+2" calculates an address and the assembler treats it like a new label. It does not add 2 to the hi byte. It may be easier to visualize if you view a disassembly of it. You will see that loop+2 refers to the location of the hi byte of <address>. Therefore, inc loop+2 increments the value of the hi byte of <address>. Quote Link to comment Share on other sites More sharing options...
NRV Posted August 21, 2011 Share Posted August 21, 2011 My generic macro to set memory to a predefined value: ;-------------------------------------------------------------------------------- ; SetMemory [address] [bytes] [value] ;-------------------------------------------------------------------------------- ; warning, using some page zero memory .macro SetMemory setMemPtr = 254 setMemCounter = 252 .if :0 <> 3 .error "SetMemory error" .else ldy #0 lda #<:1 sta setMemPtr lda #>:1 sta setMemPtr+1 .if :2 < 256 lda #:3 setMemLoop1 sta (setMemPtr),y iny cpy #:2 bne setMemLoop1 .else lda #<:2 sta setMemCounter lda #>:2 sta setMemCounter+1 setMemLoop2 lda #:3 sta (setMemPtr),y iny bne setMemB1 inc setMemPtr+1 setMemB1 lda setMemCounter bne setMemB2 dec setMemCounter+1 setMemB2 dec setMemCounter lda setMemCounter ora setMemCounter+1 bne setMemLoop2 .endif .endif .endm This doesn't need the address aligned to a page, or the number of "bytes" to be a multiply of 256.. For your example: "Let's say I want to store zeros in memory locations 32000 to 40000", the usage would be: SetMemory 32000, [40000-32000+1], 0 (yep, 8001 bytes because you specified 40000 inclusive ) This is all in MADS assembler syntax. Quote Link to comment Share on other sites More sharing options...
Tezz Posted August 21, 2011 Share Posted August 21, 2011 (edited) I do it exactly like Mapa's example for multiple pages and also similarly too with self mod when moving pages of data. If you only have a few of pages to wipe or locations that are not sequential, you can also just simply do ... lda #0 tax _lp sta <address>,x sta <address>+256,x sta <address>+512,x inx bne _lp EDIT.. I just noticed post #7 mentioning similar. P.S. What's important is getting the task done efficiently and using the least resources. Edited August 21, 2011 by Tezz Quote Link to comment Share on other sites More sharing options...
Tezz Posted August 21, 2011 Share Posted August 21, 2011 It's worth mentioning that's it's essential to have such subroutines/procedures that can be called when required. Mapa's self modifying code for the memory wipe is a good example whereby you can set the destination address before calling the procedure: lda <newaddress sta loop+1 lda >newaddress sta loop+2 jsr page_clear or with mads... mwa #$newaddress loop+1 jsr page_clear page_clear lda #0 ldx #0 ldy #<number of pages to clear> loop sta <address>,x inx bne loop inc loop+2 ; increasing HI-byte of the clearing address dey bne loop rts A preferable method would be to use A,X to hold the lsb/msb of the address before calling.. lda <startingaddress ldx >startingaddress ldy #<number of pages to clear> jsr page_clear page_clear sta loop+1 stx loop+2 lda #0 tax loop sta <address>,x inx bne loop inc loop+2 ; increasing HI-byte of the clearing address dey bne loop rts This is the method I use to copy data which I will use for example when copying data to higher memory.. mwa #<src_adr> src mwa #<dest_adr> dst ldy #$12 ; copy 18 pages from source to destination address ldx #0 move lda $ffff,x src equ *-2 sta $ffff,x dst equ *-2 inx bne move inc src+1 inc dst+1 dey bne move ... sometimes just the simple method I mentioned in post 12 which wipes 3 pages with a 19 byte proc is all you'll need, it just depends on whether or not you'll need to do something several times in your program. Quote Link to comment Share on other sites More sharing options...
texacala Posted August 23, 2011 Author Share Posted August 23, 2011 This stuff is great, so helpful. I'm starting to understand more and actually enjoy working on this! I'm sure I'll have more questions but I hope to help someone else down the road as you guys are helping me. Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.