Just Jeff Posted July 15, 2018 Share Posted July 15, 2018 Good Morning.. Is there a way to run op codes from RAM? I have a kernel band with some slack time, it would be nice to re-use it more by say- storing an op code and operand in RAM that will either change an HMMx, or COLUx, etc and somehow run it. Right now, the best thing I can think of for making the band flexible, is a jmp.ind using a pointer in RAM but I'm trying to find out if there is anything quicker. (I I also think this might force me to create giant jump tables with mostly nothing in them if I don't have a lot of things to do. Thanks! Quote Link to comment Share on other sites More sharing options...
vidak Posted July 15, 2018 Share Posted July 15, 2018 (edited) I've done some googling, and it is possible. I also know you can do self modifying code - changing the code in RAM and /then/ executing it. I think the general idea is to point the program counter to the address locations in RAM. So you'd need to set RORG to somewhere between the beginning of RAM ($80) and the end of RAM, I think. Edited July 15, 2018 by vidak 1 Quote Link to comment Share on other sites More sharing options...
+SpiceWare Posted July 15, 2018 Share Posted July 15, 2018 Yes, Thomas wrote a version on Pong that runs in RAM (can even remove cart once game is running). Read thru topic for updates & source http://atariage.com/forums/topic/143075-ram-pong 2 Quote Link to comment Share on other sites More sharing options...
tokumaru Posted July 16, 2018 Share Posted July 16, 2018 Yes, the CPU doesn't care if it's reading code from ROM or RAM, it just keeps fetching and executing them from wherever the program counter indicates. Just copy the code to RAM (or generate it on the fly if that's what you're going for) and JMP/JSR to it. 1 Quote Link to comment Share on other sites More sharing options...
ZackAttack Posted July 16, 2018 Share Posted July 16, 2018 Considering how little ram there is you may be better off using a indexed store to accomplish the same thing. ; Calculate which register to update wherever works best for you lda #HMM0 sta $80 ; Load X with the register in the kernel ldx $80 lda #$e0 sta 0,x ; Which register is written to is now dynamic If all you want to do is toggle between two registers you could use xor. I.E. COLUP0 ^ $01 = COLUP1 and COLUP1 ^ $01 = COLUP0 1 Quote Link to comment Share on other sites More sharing options...
RevEng Posted July 16, 2018 Share Posted July 16, 2018 Not part of the original question, but just something to keep in mind - you can't execute code from SARA RAM. (though emulated SARA via Harmony/Melody seems to be fine) 1 Quote Link to comment Share on other sites More sharing options...
Just Jeff Posted July 18, 2018 Author Share Posted July 18, 2018 Yes, Thomas wrote a version on Pong that runs in RAM (can even remove cart once game is running). Read thru topic for updates & source http://atariage.com/forums/topic/143075-ram-pong Interesting. I suppose then I would just jsr to RAM, then rts back.. Quote Link to comment Share on other sites More sharing options...
Just Jeff Posted July 19, 2018 Author Share Posted July 19, 2018 Considering how little ram there is you may be better off using a indexed store to accomplish the same thing. ; Calculate which register to update wherever works best for you lda #HMM0 sta $80 ; Load X with the register in the kernel ldx $80 lda #$e0 sta 0,x ; Which register is written to is now dynamic If all you want to do is toggle between two registers you could use xor. I.E. COLUP0 ^ $01 = COLUP1 and COLUP1 ^ $01 = COLUP0 I think this might satisfy most of what I was aiming for. I think I would have to add some more code that checks what scan line I'm on and if there are any updates for it. Thanks! 1 Quote Link to comment Share on other sites More sharing options...
Just Jeff Posted July 22, 2018 Author Share Posted July 22, 2018 (edited) Could I have done something leaner? Here's what I came up with so far and its a lot bigger that I thought it would be- I managed to blow up ZackAttack's code into 46 cycles to update only one register. I think it will fit because I can break it up a bit. Note: I start with 53 cycles of SLEEPs and 28 additional cycles wasted by a WSYNC and a BNE for the loop. (over the course of two lines.) My goal was to have the band highly reusable while replacing some NOPS, and have the smallest tables I could. I think it will do that. Here's a table with a few example values: SpecialInstructions: ;The first line to be changed needs to be seeded. SIRegister: ; Specify which register will be updated .byte HMP1, COLUP0, GRP0, ENABL SIValue: ; The value to be placed in the register .byte 0, YELLOW, %10101010, $02 SINext: ; The line number where the next register will be updated ; (Compared to a Decrementing ScanLine) .byte 131 .byte 110 .byte 108 And here's the code: ; Test to see if there are any register changes on this line lda ScanLine ; 3 3 See what scan line we're on cmp SILineNumRAM ; 3 6 See if it matches the stored next line to be changed ; The first one of these will have to be seeded before the kernel bne NoChanges ; 2,3 8,9 If not skip over the change routine ; if this line has a register change ; 8 stx ScratchRAMX ; 3 11 Borrow the x register for register offset sty ScratchRAMY ; 3 14 Borrow the y register for data table offset ldy SIIndexRAM ; 3 16 y is the data table offset ldx SIRegister,y ; 4 20 x is an offset that matches registers (0,x) lda SIValue,y ; 4 24 load the value that will go in the register sta $0,x ; 4 28 store the value in the register ; Now update where next change will happen lda SINext,y ; 4 32 Load the next line number that will receive an update sta SILineNumRAM ; 3 35 Store it where it will be compared to Scanline later inc SIIndexRAM ; 5 40 This will be the next table offset (y). ldx ScratchRAMX ; 3 43 Restore the x register ldy ScratchRAMY ; 3 46 Restore the y register NoChanges: ;SLEEP 30something Edited July 22, 2018 by BNE Jeff Quote Link to comment Share on other sites More sharing options...
Omegamatrix Posted July 22, 2018 Share Posted July 22, 2018 Self modifying code is very useful. I've done it many times myself as have others. In your example there does not seem to be many different values, and maybe you are doing too much for what you need. Have you considered just indexing like the example below, not running in ram? ldy siIndex lda SiLineTab,Y cmp scanline bne .skipUpdate lda RegValTab,Y ldx RegIndexTab,Y sta 0,X .skipUpdate: ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; RegIndexTab: .byte COLUP0 .byte GRP0 .byte ENABL .byte HMP1 RegValTab: .byte 0 .byte YELLOW .byte %10101010 .byte $02 SiLineTab: .byte 0 .byte 131 .byte 110 .byte 108 Alternatively, you could also stick the look up tables in ram, and keep the routine in rom. If you are limited on ram and have lots of cycles then just keep the next value from each table to be used in ram (1 byte per table). 1 Quote Link to comment Share on other sites More sharing options...
Just Jeff Posted July 23, 2018 Author Share Posted July 23, 2018 Thanks! It looks like that saves me 5 cycles.. Does this look right? stx ScratchRAMX ; 3 3 Borrow the x register for register offset sty ScratchRAMY ; 3 6 Borrow the y register for data table offset ; Test to see if there are any register changes on this line ldy SIIndex ; 3 9 y is the data table offset lda SILineNum,y ; 4 13 ; lda ScanLine ; cmp ScanLine ; 3 16 See if it matches the stored next line to be changed ; The first one of these will have to be seeded before the kernel bne NoChanges ; 2,3 18,19 If not skip over the change routine ; if this line has a register change ; 8 ldx SIRegister,y ; 4 22 x is an offset that matches registers (0,x) lda SIValue,y ; 4 26 load the value that will go in the register sta $0,x ; 4 30 store the operand in the register ; Now update where for next change will happen ;lda SINext,y ; Load the next line number that will receive an update ;sta SILineNumRAM ; Store it where it will be compared to Scanline later inc SIIndexRAM ; 5 35 This will be the next table offset. ldx ScratchRAMX ; 3 38 Restore the x register ldy ScratchRAMY ; 3 41 Restore the y register NoChanges: ;SLEEP 30something Quote Link to comment Share on other sites More sharing options...
+SvOlli Posted July 27, 2018 Share Posted July 27, 2018 Sidenote: running code from cartridge RAM is a different topic, though: the SARA-RAM is too slow and can't handle being accessed on two back-to-back cycles, like lda #$00. CommaVid RAM does work though, since it's the main cause of the CommaVid cartridge. Wanted to try this one in a demo, if only I had more time... 2 Quote Link to comment Share on other sites More sharing options...
Thomas Jentzsch Posted July 27, 2018 Share Posted July 27, 2018 How many cycles per byte is the minimum then? 2? And what happens when you read too fast? Has this been analyzed? Quote Link to comment Share on other sites More sharing options...
+SvOlli Posted July 29, 2018 Share Posted July 29, 2018 I remember reading documentation on SARA stating that it can handle 300kHz. Roughly calculated: 4 cycles per byte, so that's why LDA $F080 : LDA $F081 should work. Never verified it due to not having access to the hardware (in "development form") 1 Quote Link to comment Share on other sites More sharing options...
alex_79 Posted July 29, 2018 Share Posted July 29, 2018 I only have this brief overview about SARA chip in my 2600-related documents: I suppose that these limitations don't apply to superchip games running on a Melody/Harmony. 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.