JeremiahK Posted December 14, 2017 Author Share Posted December 14, 2017 Excellent idea, as always! So the highest 3 bits of the address are ignored? That can be useful. I love how the VCS has all these "features" as a byproduct of cost-cutting, though at the end of the day I would much rather have more than 4K of addressable space. 1 Quote Link to comment Share on other sites More sharing options...
CPUWIZ Posted December 14, 2017 Share Posted December 14, 2017 Excellent idea, as always! So the highest 3 bits of the address are ignored? That can be useful. I love how the VCS has all these "features" as a byproduct of cost-cutting, though at the end of the day I would much rather have more than 4K of addressable space. The real reason is the address bus on the physical chip, which is also why it is smaller than a regular 6502. Quote Link to comment Share on other sites More sharing options...
JeremiahK Posted December 14, 2017 Author Share Posted December 14, 2017 Actually, 3 ROLs and an AND #$07 would be quicker than 5 LSRs. Quote Link to comment Share on other sites More sharing options...
+SpiceWare Posted December 14, 2017 Share Posted December 14, 2017 Actually, 3 ROLs and an AND #$07 would be quicker than 5 LSRs. Actually, cycles would be the same as you'd need to use 4 ROLs (as bit 7 goes into C, not bit 0), but due to the AND #$07 you'd use 1 extra byte of ROM. 1 Quote Link to comment Share on other sites More sharing options...
JeremiahK Posted December 14, 2017 Author Share Posted December 14, 2017 (edited) Aha, good to clear that up, I didn't realize that was how ROR/ROL worked. The real reason is the address bus on the physical chip, which is also why it is smaller than a regular 6502. Because those bits on the address bus aren't even used, correct? Meaning the CPU itself thinks it can access 16-bit memory addresses, but only 13 of the "lanes" or "pins" are actually connected to anything. Edited December 14, 2017 by JeremiahK Quote Link to comment Share on other sites More sharing options...
+SpiceWare Posted December 14, 2017 Share Posted December 14, 2017 Yep, those line's aren't connected. Quote Link to comment Share on other sites More sharing options...
vidak Posted December 14, 2017 Share Posted December 14, 2017 (edited) I am learning a lot by lurking on this thread B-) Edited December 14, 2017 by vidak Quote Link to comment Share on other sites More sharing options...
JeremiahK Posted December 15, 2017 Author Share Posted December 15, 2017 I have done some minor optimizations to the code. I improved the efficiency of the code that sets the 5 object positions for the scoreboard display, restructuring reads and writes and timing to use the fewest number of writes to registers. There are still long SLEEP commands, but that is easily fixed once I know what to fill in those gaps with. I re-wrote the routine that pushes the scoreboard graphics onto the stack, and cut it down from spending 1245 cycles to 685 cycles. This of course is at the expense of some ROM space, but no extra RAM was needed for the pointers because I am sharing the RAM with other variables. I also have it set up in such a way that wouldn't mess up my ability to use the highest 3 bits of the pointers for other things. I moved the 4K code segment from $F000 to $1000 so that all the labels would have 0's the highest 3 bits, as well. Here is the code for the new scoreboard loading routine. I changed the Level variable from BCD mode to binary mode, so I could potentially use the highest 3 bits for something else. I just realized that this is not neccesary, because the range is only from 0-19 anyway. I will change it back to BCD tomorrow. ; <><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><> ; Load Scoreboard ; ; Get graphics data for the scoreboard and push it onto the stack ; ; Takes 685 cycles to complete (9 full scanlines + 1 cycle) ; <><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><> SUBROUTINE ; <><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><> ; Prepare pointer for level digit graphics lda #>LevelGfx ; 2 sta LevelLoadPtr+1 ; 3 - set MSB of level digit graphics pointer lda Level ; 3 ; if I change the level variable back to BCD mode, mod 10 will be easier ; ; and #$0F ; that's it sec ; 2 - perform a mod 10 to isolate left digit sbc #10 ; 2 bcc .Negative ; 3/2 bcs .Positive ; 3 - done this way to use the same number of cycles ; either way, may or may not be neccesary .Negative adc #10 ; 2 .Positive asl ; 2 asl ; 2 asl ; 2 adc #<LevelGfx ; 2 - add graphics table offset sta LevelLoadPtr ; 3 - set LSB of level digit graphics pointer ; <><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><> ; Prepare one pointer MSB and multiple LSB's for score digit graphics lda #>ScoreGfx ; 2 sta ScoreLoadPtr+1 ; 3 - set MSB of score digit graphics pointer lax BCDScore+0 ; 3 and #$F0 ; 2 lsr ; 2 sta ScoreDigit0 ; 3 - set LSB for digit 0 txa ; 2 and #$0F ; 2 asl ; 2 asl ; 2 asl ; 2 sta ScoreDigit1 ; 3 - set LSB for digit 1 lax BCDScore+1 ; 3 and #$F0 ; 2 lsr ; 2 sta ScoreDigit2 ; 3 - set LSB for digit 2 txa ; 2 and #$0F ; 2 asl ; 2 asl ; 2 asl ; 2 sta ScoreDigit3 ; 2 - set LSB for digit 3 lax BCDScore+2 ; 3 and #$F0 ; 2 lsr ; 2 sta ScoreDigit4 ; 3 - set LSB for digit 4 txa ; 2 and #$0F ; 2 asl ; 2 asl ; 2 asl ; 2 sta ScoreDigit5 ; 3 - set LSB for digit 5 ; <><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><> ; Load the stack with the graphics for the scoreboard ldy #6 ; 2 .LoadScoreboard lda (LevelLoadPtr),y; 5 pha ; 3 lda ScoreDigit5 ; 3 sta ScoreLoadPtr ; 3 lda (ScoreLoadPtr),y; 5 pha ; 3 lda ScoreDigit4 ; 3 sta ScoreLoadPtr ; 3 lda (ScoreLoadPtr),y; 5 pha ; 3 lda ScoreDigit3 ; 3 sta ScoreLoadPtr ; 3 lda (ScoreLoadPtr),y; 5 pha ; 3 lda ScoreDigit2 ; 3 sta ScoreLoadPtr ; 3 lda (ScoreLoadPtr),y; 5 pha ; 3 lda ScoreDigit1 ; 3 sta ScoreLoadPtr ; 3 lda (ScoreLoadPtr),y; 5 pha ; 3 lda ScoreDigit0 ; 3 sta ScoreLoadPtr ; 3 lda (ScoreLoadPtr),y; 5 pha ; 3 dey ; 2 bpl .LoadScoreboard ; 2/3 Quote Link to comment Share on other sites More sharing options...
JeremiahK Posted December 23, 2017 Author Share Posted December 23, 2017 (edited) I re-wrote the scoreboard loading routine again to use 7 actual pointers, rather than 1 for the level counter graphics and 1 recycled over and over for each score digit's graphics. Now it takes only 535 cycles, and a few less bytes of ROM. I also improved many other areas of the code, especially the scoreboard kernel. I have added support for a "level color" which controls the color of the scoreboard, progress/health display, and throbbing lines between rows, so each level can have it's own color. I was planning on rearanging some of the pages, but since I still have a few pages left, I will wait a bit to do that. It's starting to look like this will turn into an 8K project. I have a couple ideas of things to add when that happens, such as a detailed game select screen. Epilepsy warning: this demo quickly loops through all the colors to demonstrate the level color control, causing full-screen craziness. nyancat.bin Edited December 23, 2017 by JeremiahK 2 Quote Link to comment Share on other sites More sharing options...
JeremiahK Posted December 27, 2017 Author Share Posted December 27, 2017 (edited) I hope you all had a Merry Christmas! I spent today working on (eventually) making the cat able to move vertically. I haven't explained exactly how I will do this yet, so I will now. I have the graphics tables for the cat's face, the cat's tart body, and the rainbow colors set up in such a way that they are padded with 0's. This way, I can easily move the cat vertically by moving the pointers' positions in the graphics tables and simply update the graphics every scanline, rather than checking each scanline for when to start drawing them, which I don't have time for. When I first planned this, I thought I would need to have one big kernel loop for the whole screen, which would use up a ton of ROM for all the 0's in the graphics tables. That's when I decided to split the kernel into sections, drawing cat-less rows above and below the cat's two rows (the cat needs two rows so it can be positioned halfway between two rows). This way, the graphics tables can be much smaller, since they are only used for 33 scanlines (14 per row, plus 5 for the throbbing row seperator). I wrote out a complete timing plan for the cat's kernel. This kernel is tight! Every cycle is used, and I needed to move both the rainbow color graphics and the cat's tart graphics into RAM, saving 3 cycles, to make it possible. If I used a scanline counter with a dey/bne loop for each of the two rows, I would have to move the graphics pointers before drawing the throbbing line, which isn't possible. So instead, I am using a single counter for the whole thing. I added a special byte to the end of each food item's graphics (making sure it's never used in the graphics themselves) so I can use a cmp/bne loop, using the uniform height of the food items to know when to break from the loop. I haven't made a working demo yet, as I still need to write code to load the tables into RAM. I will need to figure out a way that uses as little RAM as possible. ; Stack pointer is pre-loaded with 2nd food item's color ; END_FOOD is any value that isn't present in any of the food item's graphics ; <><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><> ; Kernel used when food items are closer to the left edge of the screen ; Playfield must be set to MIRRORED mode ; <><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><> .LeftLoop ; 3 cycles sta GRP1 ; 03 - set 1st food item's gfx ; 7 cycles lda RamBowColors,y ; 07 - set rainbow colors sta COLUBK ; 10 ; 7 cycles lda RamBowColors+1,y; 14 sta COLUPF ; 17 ; 6 cycles lda FoodColor1 ; 20 - set 1st food item's color sta COLUP1 ; 23 - (1 color clock late, but that's fine with me) ; 8 cycles lda #COL_CAT_TART ; 25 sta COLUPF ; 28 - MUST set tart color at cycle 28 stx COLUBK ; 31 - MUST set face/bg color to black at cycle 31 ; 13 cycles lda (FoodGfxPtr2),y ; 35 - load 2nd food item's gfx tsx ; 38 - load 2nd food item's color sta GRP1 ; 41 - set 2nd food item's gfx stx COLUP1 ; 44 - set 2nd food item's color ; 2 cycles dey ; 46 - succeeding reads/writes are for next scanline .LeftEntrance ; enter loop here ; 5 cycles ldx #COL_BACKGROUND ; 48 - X must be set to 0 (black) stx COLUPF ; 51 - don't draw rainbow/tart on right side ; 8 cycles lda (CatGfxPtr),y ; 56 - set cat's head gfx sta GRP0 ; 59 ; 7 cycles lda RamTartGfx,y ; 63 - set cat's tart gfx sta PF1 ; 66 ; 10 cycles lda (FoodGfxPtr1),y ; 71 - load 1st food item's gfx cmp #END_FOOD ; 73 - if gfx != END_FOOD... bne .LeftLoop ; 00/75 - ...draw this row's next scanline... bmi .End ; 02 - ...otherwise row is complete ; <><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><> ; Kernel used when food items are closer to the right edge of the screen ; Playfield must be set to COPIED mode ; <><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><> .RightLoop ; 3 cycles sta GRP1 ; 10 03 - set 1st food item's gfx ; 7 cycles lda RamBowColors+1,y; 14 - set background's rainbow color sta COLUPF ; 17 - (1 color clock late, but that's fine with me) ; 6 cycles lda FoodColor1 ; 20 - set 1st food item's color sta COLUP1 ; 23 ; 8 cycles lda #COL_CAT_TART ; 25 sta COLUPF ; 28 - MUST set tart color at cycle 28 stx COLUBK ; 31 - MUST set face/bg color to black at cycle 31 stx COLUPF ; 34 - don't draw rainbow/tart on right side ; 5 cycles lax (FoodGfxPtr2),y ; 39 - load 2nd food item's gfx before dey ; 2 cycles dey ; 41 - succeeding reads/writes are for next scanline ; 7 cycles lda RamTartGfx,y ; 45 - set cat's tart gfx sta PF1 ; 48 ; 4 cycles lda RamBowColors+1,y; 52 - use 4 cycles to load playfield's rainbow color ; 8 cycles stx GRP1 ; 55 - set 2nd food item's gfx no sooner than cycle 55 tsx ; 57 - load 2nd food item's color stx COLUP1 ; 60 - set 2nd food item's color no later than cycle 60 .RightEntrance ; enter loop here ; 2 cycles ldx #COL_BACKGROUND ; 62 - X register must be set to 0 (black) ; 3 cycles sta COLUPF ; 65 - set PF's rainbow color no sooner than cycle 59 ; 8 cycles lda (CatGfxPtr),y ; 70 - set cat's head gfx sta GRP0 ; 73 ; 10 cycles lda (FoodGfxPtr1),y ; 02 - load 1st food item's gfx cmp #END_FOOD ; 04 - if gfx != END_FOOD... bne .RightLoop ; 07/06 - ...draw this row's next scanline... ; ...otherwise row is complete ; <><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><> .End Edited December 27, 2017 by JeremiahK Quote Link to comment Share on other sites More sharing options...
ZackAttack Posted December 27, 2017 Share Posted December 27, 2017 Could you just use 0 for the sentinel value and omit the cmp #END_FOOD? Seems like that would free up a couple cycles. Quote Link to comment Share on other sites More sharing options...
JeremiahK Posted December 27, 2017 Author Share Posted December 27, 2017 Unfortunately no. The value 0 is used in some of the food item's graphics. Bit 7 is also used, so I can't use a BPL loop unless I limit the graphics to 7 bits wide, which I'd rather not have to do. Quote Link to comment Share on other sites More sharing options...
ZackAttack Posted December 27, 2017 Share Posted December 27, 2017 0 can be replaced with $ff and color change? This is why Atari programming is great. Spending so much effort on 2 cycles would surely get you fired in any modern programming job. 1 Quote Link to comment Share on other sites More sharing options...
JeremiahK Posted December 27, 2017 Author Share Posted December 27, 2017 "0 can be replaced with $ff and color change?" That might actually work, with some modifications to the graphics. The only problem is that the black square for blank items will end up being drawn over the cat and rainbow at certain times, but I think that could be fixed by changing the playfield priority when necessary. Quote Link to comment Share on other sites More sharing options...
JeremiahK Posted December 27, 2017 Author Share Posted December 27, 2017 It would still affect the parts of the rainbow drawn with the background, but I don't have to use $ff as the value, I could use $80 or $01, for instance. Quote Link to comment Share on other sites More sharing options...
JeremiahK Posted December 28, 2017 Author Share Posted December 28, 2017 I realized that this plan won't work. It would draw the cat correctly, but not the food items. I will just have to use another couple pointers for the 2nd row's cat graphics. I might be able to share these pointers with the health logo pointers. I modified the kernel, and it does work as it should. I had a free cycle, so I used it to load the tart color from RAM, allowing me to change the cat's color. Now I can add a customizable cat color, and do a flashing animation when you lose health. Quote Link to comment Share on other sites More sharing options...
JeremiahK Posted January 2, 2018 Author Share Posted January 2, 2018 Happy 2018! Wow, I completely underestimated how complicated it would be to draw Nyan Cat with the rainbow, with two unique food items on the same row, and then continue to draw Nyan Cat with the rainbow over the throbbing lines, all while setting up the position of the next food items, and then draw another cat/rainbow/food row. But I did it! Here is a full grid of swift fruit and veggies, with a moving cat and rainbow! Right now I can't put the cat on the top or bottom row, but that is a quick fix. I just wanted to keep you all posted! nyancat.bin 5 Quote Link to comment Share on other sites More sharing options...
+Karl G Posted January 2, 2018 Share Posted January 2, 2018 Wow! It is looking really awesome! 1 Quote Link to comment Share on other sites More sharing options...
+SpiceWare Posted January 2, 2018 Share Posted January 2, 2018 Looking good! Clever use of the missiles and ball for what I presume is the level indicator. Have the digits look different is a bit odd though. A couple options for that: change score font draw it with the players To draw it with the player's you'd need to change NUSIZ# twice per scanline alternating between 3 copies close and 2 copies wide. I'm not sure if there's enough time to change both players without the use of something like DPC+. 1 Quote Link to comment Share on other sites More sharing options...
JeremiahK Posted January 3, 2018 Author Share Posted January 3, 2018 Looking good! Clever use of the missiles and ball for what I presume is the level indicator. Have the digits look different is a bit odd though. A couple options for that: change score font draw it with the players To draw it with the player's you'd need to change NUSIZ# twice per scanline alternating between 3 copies close and 2 copies wide. I'm not sure if there's enough time to change both players without the use of something like DPC+. Yep, that's the level indicator. It can display any number from 0-19, although I'm not planning on using 0 in the game. That's pretty genius with the 2 copies wide, I'll have to look into that. Maybe I can get rid of the use of the collision registers to know when to break from the loop, although I kind of like it that way. As it is now, I have all the graphics pre-loaded into the stack so I can save 7 cycles per scanline. This uses 49 bytes of RAM, but some of the bytes can be reused for variables in the gameplay kernel, plus 34 bytes for the rainbow. Is there any way to do a 6-digit score without enabling the VDELPx registers? That could save me some cycles. Quote Link to comment Share on other sites More sharing options...
+SpiceWare Posted January 3, 2018 Share Posted January 3, 2018 Maybe I can get rid of the use of the collision registers to know when to break from the loop, although I kind of like it that way. I was wondering about that, the score added some extra scanlines when I was disabling objects to analyze your kernel. Is there any way to do a 6-digit score without enabling the VDELPx registers? That could save me some cycles. Time Warp from back in the day has a 6 digit score without using VDEL via Bus Stuffing: ShowGraphic: ; X = # of rows to output SGloop: sta WSYNC ;--------------------------------------- sty GRP0 ; 3 3 sty GRP1 ; 3 6 SLEEP 33 ;33 39 dex ; 2 41 sty GRP0 ; 3 44 sty GRP1 ; 3 47 sty GRP0 ; 3 50 sty GRP1 ; 3 53 bne SGloop ; 2 55 (3 56) sta WSYNC ;--------------------------------------- stx GRP1 ; 3 3 GRP0 now blank (as X was 0) stx GRP0 ; 3 6 GRP1 now blank rts 1 Quote Link to comment Share on other sites More sharing options...
JeremiahK Posted January 3, 2018 Author Share Posted January 3, 2018 (edited) Here's the code I thought up in bed last night. Unfortunately, it doesn't quite work. ; 66 ldy #6 ; 2 68 .ScoreboardLoop ; 5 cycles to set NUSIZ1 for score lda #3 ; 2 70 sta NUSIZ1 ; 3 73 ; 7 cycles to load Temp with level digit lda LevelDigit,y ; 4 01 sta Temp ; 3 04 ; 6 cycles to load Y with digit 5 lda Digit5,y ; 4 08 tay ; 2 10 ; 6 cycles to load stack pointer with digit 6 ldx Digit6,y ; 4 14 txs ; 2 16 ; 14 cycles to load GRPx's with digits 1 and 2 lda Digit1,y ; 4 20 ldx Digit2,y ; 4 24 sta GRP0 ; 3 27 stx GRP1 ; 3 30 ; 8 cycles to load A and X with digits 3 and 4 lda Digit3,y ; 4 34 ldx Digit4,y ; 4 38 ; 14 cycles to write to GRPx's for score sta GRP0 ; 3 41 stx GRP1 ; 3 44 sty GRP0 ; 3 47 tsx ; 2 49 stx GRP1 ; 3 52 ; 5 cycles to set NUSIZ1 for level digit lda #4 ; 2 54 sta NUSIZ1 ; 3 57 ; 6 cycles to load GRP1 with level digit lda Temp ; 3 60 sta GRP1 ; 3 63 ; 5 cycles for loop management dey ; 2 65 bpl .ScoreboardLoop ; 3/2 68 I forgot that Y gets loaded with graphics, so it can't be used as the loop counter. If the code were rearranged a bit and changed to use a RAM variable to control the loop, it should be possible to have a tightly-spaced 6-digit score without using VDEL, but not with another digit using NUSIZ. Also, the leftmost graphics bit would have to be cleared in the graphics, but that's not really a problem because it would be neccesary anyway with digits that close together. It should actually be possible to do both the score and level counter if I position the score on the right so that the level digit would wrap around to the left side, but I think that would look funny. Edited January 3, 2018 by JeremiahK Quote Link to comment Share on other sites More sharing options...
+SpiceWare Posted January 3, 2018 Share Posted January 3, 2018 Omegamatrix was able to do a 48 pixel display using INTIM as the loop control <searching>: It works!! I've successfully gotten a routine running. I laid out graphics for digits 0-8 from $FE00 to $FE4B, and graphics for digit 9 & letters A-H from $FE4C to $FE97. You can add more very easily. I was too tired to do this for the demo, but you will get the idea. I am only doing a loop of 8 scanlines in height, but you can do more. I layed it out as bunch of equates to make it easier to read. I'll see what you guys think. Macro's would be a good idea as well as a command line utility as Spiceware suggested. This saves 4 cycles, and a zero page ram location over a regular 48 bit display. In the demo the top numbers are generated by a standard 48 bit display, and the bottom numbers are done by the faster (uses less cycles) 48 bit routine. testTIM8T.png testTIM8T.zip Emphasis added, might prove useful to what you're trying to do. Hit the little curled arrow in the top-right of the quoted text to see the original reply in context as there's a bit more about this in that topic. Quote Link to comment Share on other sites More sharing options...
Thomas Jentzsch Posted January 3, 2018 Share Posted January 3, 2018 Yup, works well. I used that trick in Three.s too. Quote Link to comment Share on other sites More sharing options...
JeremiahK Posted January 3, 2018 Author Share Posted January 3, 2018 Going to the gym, will look later. Sounds promising 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.