Urgent help needed to condense code for VGA (buffer?) and counting display code
WBA Consulting
Posts: 2,934
I have a project that I am working on that is due Saturday morning for a Christmas play this weekend. My code is going very well, but I have run into a snag with running out of memory and I still need to add code for the xBee module that will receive triggers from a remote for each particular event. There are a couple places that I think I can reduce my code usage to save some small bits, but I have realized that the VGA code I am using is taking up a significant amount of space.
I am using BIG VGA Font from this thread, and in the VAR section, a LONG array called BITMAP is being declared that consumes 3072+64 longs. I don't fully understand how this bitmap buffer is utilized, so I am uncertain, but can this be reduced? It appears that this is my biggest memory killer.
Also, there is a LONG array called colors being specified at 64 longs. Do I need this variable that large when I am only using 4 colors?
One section I am going to condense is for my counting display sequence. Currently, I have each year-to-year sequence separately coded, but I know I can condense them by using one routine that uses variables for the differences so the code can be the same. With that, however, I need to figure out how to make the code count from one year to the next in a specific amount of time when the difference between the years varies (one jump is 2013 to 1978 (35 years) , but the next jump is only 1978 to 1985 (7 years). The sound effect that plays while the time machine is counting is 24 seconds long. The counting display needs to last 18 seconds. Below is my current code sequence for jumping from 2013 to 1978. It times out perfectly by use of the delay while counting, but I have to change this delay for each jump.
Also, it is hardcoded to count down, but the next jump needs to count up. There are a total of 8 separate years to travel to: Start at 2013, then 1978, 1985, 1959, 1998, 1944, 0, 1944,and finally back to 2013. I will probably need to have a special routine for jumping between 1944 and 0 due to the high number of years to count.
Here is the code for my version 008 demo that works for a keypad entry sequence, two time jumps with the sound file, and then displays "the end". Only the time jump for 1978 routine is commented fully.
I am using BIG VGA Font from this thread, and in the VAR section, a LONG array called BITMAP is being declared that consumes 3072+64 longs. I don't fully understand how this bitmap buffer is utilized, so I am uncertain, but can this be reduced? It appears that this is my biggest memory killer.
VAR long bitmap[3072+64] ' Must be aligned on 32 byte boundary long colors[64] word screen[x_tiles * y_tiles] long bitmapAddr byte numStr[5]
Also, there is a LONG array called colors being specified at 64 longs. Do I need this variable that large when I am only using 4 colors?
One section I am going to condense is for my counting display sequence. Currently, I have each year-to-year sequence separately coded, but I know I can condense them by using one routine that uses variables for the differences so the code can be the same. With that, however, I need to figure out how to make the code count from one year to the next in a specific amount of time when the difference between the years varies (one jump is 2013 to 1978 (35 years) , but the next jump is only 1978 to 1985 (7 years). The sound effect that plays while the time machine is counting is 24 seconds long. The counting display needs to last 18 seconds. Below is my current code sequence for jumping from 2013 to 1978. It times out perfectly by use of the delay while counting, but I have to change this delay for each jump.
PUB JUMP1978 | i wav.play(string("SFX.wav")) ' Play 24 second long "time jump" sound effect waitcnt(cnt+clkfreq*2) ' Let SFX volume fade up JUMPSTANDBY ' Show time machine activation/standby display waitcnt(cnt+clkfreq*2) ' and leave up for 2 seconds before counting ' Count from present year to jump year i:=2013 ' this repeat loop must complete in ~18 seconds to match sound file! repeat until i == 1977 ' stop at 1978 num2str(i) gr.textmode(9,9,6,5) gr.colorwidth(1,3) gr.text(0,10,@numStr) waitcnt(cnt+clkfreq/3) ' delay between years during counting i-=1 ' increment year down (how to handle both directions?????) gr.colorwidth(0,8) gr.box(-120,-30,240,81) ' WAV sound effect fades out about here ' Flash current year ' done counting, flash year of arrival 8 times repeat 8 gr.colorwidth(0,8) gr.box(-120,-30,240,81) ' draw black box to clear year waitcnt(cnt+clkfreq/4) gr.textmode(9,9,6,5) gr.colorwidth(1,8) gr.text(0,10,@numStr) ' draw year waitcnt(cnt+clkfreq)
Also, it is hardcoded to count down, but the next jump needs to count up. There are a total of 8 separate years to travel to: Start at 2013, then 1978, 1985, 1959, 1998, 1944, 0, 1944,and finally back to 2013. I will probably need to have a special routine for jumping between 1944 and 0 due to the high number of years to count.
Here is the code for my version 008 demo that works for a keypad entry sequence, two time jumps with the sound file, and then displays "the end". Only the time jump for 1978 routine is commented fully.
' TIME MACHINE ' WBA Consulting ' Andrew Williams ' (c)2013 ' ' Code emulates a Time Machine Prop for a Church Christmas Kids Play. ' Uses WAV files for sound effects, drives a VGA display, and lights some LEDs ' ' Version Info: ' 007 Working code for demo of display setup and sound files ' 008 Cleaned Structure, fixed formatting discrepancies ' 009 ***Need to add other years*** ' CON _clkmode = xtal1 + pll16x _xinfreq = 5_000_000 x_tiles = 16 y_tiles = 12 'WAV Player lPin = 26 rPin = 27 doPin = 22 clkPin = 23 diPin = 24 csPin = 25 wpPin = -1 cdPin = -1 VAR long bitmap[3072+64] ' Must be aligned on 32 byte boundary long colors[64] word screen[x_tiles * y_tiles] long bitmapAddr byte numStr[5] OBJ vga : "vga" gr : "graphics" wav : "V2-WAV_DACEngine.spin" PUB start '| i 'VGA Initilization StartVGA 'WAV Player Initilization wav.begin(lPin, rPin, doPin, clkPin, diPin, csPin, wpPin, cdPin) waitcnt(cnt+clkfreq*2) 'Code SYSBOOT repeat 6 gr.textmode(4,4,6,0) gr.colorwidth(1,3) gr.text(-70,10,string("#>_")) waitcnt(cnt+clkfreq/3) gr.colorwidth(0,3) gr.text(-70,10,string(" _")) waitcnt(cnt+clkfreq/4) DATAENTRY waitcnt(cnt+clkfreq*4) JUMP1978 waitcnt(cnt+clkfreq*4) JUMP1985 ' the end waitcnt(cnt+clkfreq*8) gr.clear gr.colorwidth(1,4) gr.box(-120,-30,240,81) gr.textmode(4,4,6,5) gr.colorwidth(2,3) gr.text(0,10,string("the end")) PUB SYSBOOT gr.textmode(3,3,6,5) gr.colorwidth(1,3) gr.text(-40,80,string("SYS READY")) gr.textmode(2,2,6,5) gr.colorwidth(2,2) gr.text(-70,-70,string("HO-2000")) gr.textmode(2,2,6,5) gr.colorwidth(2, 2) gr.text(50, -70, string("DEPART")) PUB SYSREADY gr.textmode(3,3,6,5) gr.colorwidth(1,3) gr.text(-40,80,string("SYS READY")) gr.textmode(2,2,6,5) gr.colorwidth(2,2) gr.text(-70,-70,string("HO-2000")) PUB JUMPSTANDBY gr.clear gr.textmode(3,3,6,5) gr.colorwidth(1,3) gr.text(0,75,string("12 / 24")) gr.textmode(9,9,6,5) gr.colorwidth(1,3) gr.text(0,10,string("2013")) gr.textmode(2,2,6,5) gr.colorwidth(2,2) gr.text(-70,-70,string("HO-2000")) gr.textmode(2,2,6,5) gr.colorwidth(3, 2) gr.text(50, -70, string("12/24/----")) PUB DATAENTRY gr.clear SYSREADY wav.play(string("keys.wav")) gr.textmode(4,4,6,0) gr.colorwidth(1,3) gr.text(-70,10,string("#>1")) waitcnt(cnt+clkfreq/4*3) gr.text(-70,10,string("#>12")) waitcnt(cnt+clkfreq/4*3) gr.clear SYSREADY gr.textmode(2,2,6,5) gr.colorwidth(3, 2) gr.text(50, -70, string("12/--/----")) gr.colorwidth(1,3) gr.textmode(4,4,6,0) waitcnt(cnt+clkfreq/4) gr.text(-70,10,string("#>2")) waitcnt(cnt+clkfreq/4) gr.text(-70,10,string("#>24")) waitcnt(cnt+clkfreq/4*3) gr.clear SYSREADY gr.textmode(2,2,6,5) gr.colorwidth(3, 2) gr.text(50, -70, string("12/24/----")) gr.textmode(4,4,6,0) gr.colorwidth(1,3) gr.text(-70,10,string("#> ")) PUB JUMP1978 | i wav.play(string("SFX.wav")) ' Play 24 second long "time jump" sound effect waitcnt(cnt+clkfreq*2) ' Let SFX get going JUMPSTANDBY ' Show time machine activation/standby display waitcnt(cnt+clkfreq*2) ' and leave up for 2 seconds before counting ' Count from present year to jump year i:=2013 ' this repeat loop must complete in ~18 seconds repeat until i == 1977 ' stop at 1978 num2str(i) gr.textmode(9,9,6,5) gr.colorwidth(1,3) gr.text(0,10,@numStr) waitcnt(cnt+clkfreq/3) ' delay between years during counting i-=1 gr.colorwidth(0,8) gr.box(-120,-30,240,81) ' WAV sound effect fades out about here ' Flash current year ' done counting, flash year of arrival 8 times repeat 8 gr.colorwidth(0,8) gr.box(-120,-30,240,81) ' draw black box to clear year waitcnt(cnt+clkfreq/4) gr.textmode(9,9,6,5) gr.colorwidth(1,8) gr.text(0,10,@numStr) ' draw year waitcnt(cnt+clkfreq) PUB JUMP1985 | i wav.play(string("SFX.wav")) gr.colorwidth(0,8) ' clear bolded year gr.box(-120,-30,240,81) ' Count from present year to jump year i:=1978 ' current year repeat until i == 1986 num2str(i) gr.textmode(9,9,6,5) gr.colorwidth(1,3) gr.text(0,10,@numStr) if i == 1978 waitcnt(cnt+clkfreq*9) else waitcnt(cnt+clkfreq) i+=1 gr.colorwidth(0,8) gr.box(-120,-30,240,81) ' Flash current year repeat 8 gr.colorwidth(0,8) gr.box(-120,-30,240,81) waitcnt(cnt+clkfreq/4) gr.textmode(9,9,6,5) gr.colorwidth(1,8) gr.text(0,10,@numStr) waitcnt(cnt+clkfreq) PUB num2str(value) | given ' For numbers from 100 to 9999 given := value if value < 1000 value *= 10 numStr[0] := value / 1000 + "0" value //= 1000 numStr[1] := value / 100 + "0" value //= 100 numStr[2] := value / 10 + "0" value //= 10 numStr[3] := value + "0" numStr[4] := 0 if given < 1000 numStr[3] := 0 PUB StartVGA | i, dx, dy, c bitmapAddr := (@bitmap + $3F) & $FFFF_FFC0 ' Bitmap address must start on a 32 byte boundary 'start vga vga_videobase := @screen vga_colorbase := @colors vga.start(@vgaparams) 'init colors repeat i from 0 to 63 colors[i] := $28_48_FF_00 ' teal, purple, white, black 'init tile screen repeat dx from 0 to vga_hc - 1 repeat dy from 0 to vga_vc - 1 screen[dy * vga_hc + dx] := bitmapAddr >> 6 + dy + dx * vga_vc '+ ((dy & $3F) << 10) 'start and setup graphics gr.start gr.setup(16, 12, 128, 96, bitmapAddr) DAT vgaparams long 0 'status long 1 'enable long 1_111 'pins long 11 'mode vga_videobase long 0 'videobase vga_colorbase long 0 'colorbase vga_hc long x_tiles 'hc vga_vc long y_tiles 'vc long 2 'hx //RJA increased from 1X to 2X long 2 'vx //RJA increased from 1X to 2X long 0 'ho long 0 'vo long 512 'hd long 16 'hf long 96 'hs long 48 'hb long 380+16 'vd //RJA added 16 long 11 'vf long 2 'vs long 31+16 'vb //RJA subtracted 16 long 20_000_000 'rate
Comments
Thanks for the BST tip! Compiling in BST with the two options you mention increases the free longs from 220 to 654, so it definitely does help. I might be able to make that work. I have a lot of code to finish out to know what I truly need, but adding simple serial for the XB and two more time jumps wiped me out originally.
I think my next hurdle is figuring out how to re-use the jump loop code or finish off the XB code to see where I stand. I am worried that I may end up in a memory crunch again though....
Just something to keep in mind if you can't squeeze everything you need/want into the remaining memory.
Also, try creating a single method for the waitcnts like this, see if this saves a few longs:
Duane, Yeah, I had thought of that. I could use my Prop Activity Board for the audio and XB then use the PropBOE for the video. I hope I don't have to go that route as that adds more time, more testing, etc. I may try to complete everything but the video display to get it "done" then try to add the video.
You don't need all the colors array longs. If you are only using 4 screen colors, then you can cut that array down to just one long, if you want to. How it works is each tile gets a 4 color palette. The 64 longs represent 64 possible 4 color palettes for the tiles. Maybe keep two longs, and you could use 8 colors, if you want to, etc...
In my signature, I've got a link there "Parallax Colors Simplified" and it applies to this tile type driver you are using. There are methods in there to set the screen colors, show how the color array is used, etc...
This code sets all the palette entries to the same 4 colors.
BTW, once you have done this, you can then change those 4 colors dynamically and the entire screen changes. You can set one to black, or your background color for example. Say color 00 = black. Say color 01 = red, but you want to draw a lot of stuff, then have it pop onto the screen. Set color 01 = black, draw it all, then set it to red. Everything will appear all at once.
You want:
Then you want all the tiles assigned to the same palette entry!
(more in a minute)
I'm having trouble pasting into a code window on this Mac.
Just know that this code:
Initializes all the tiles. Each tile points to a little region of HUB RAM, and each tile references one of those color palettes. That complex bit of code assigns lots of color palettes. This is undesirable when using a 4 color screen.
Use the methods in the programs you find in my signature to see how the tile address and color palette get defined and then define all your tiles to the same screen layout, but make sure they all point to just one color entry and you save those longs. If you change the variable names and color definitions, they should work with this driver you are using.
Edit: Looks like you are already running single buffer on that one.
That means the 3072 longs are needed for the 256x192 screen. If you want some savings, you could get rid of a row or two of tiles in the "X" direction. Each tile is 16 pixels wide, and each row removed will recover 768 bytes for you.
Remove one row, and you've got a 240 pixel display. Compensate by increasing the vgaparams to better center the tiles on the display. I can't run this right now, or I would.
These also can't be in the upper 64K. The VGA driver needs them in the HUB.
Between those two things, you should be able to cram things in next to the video.
More savings is possible. If you need it, let me know, but the next thing to do is play tricks with the tiles. Say you have blank areas on the screen. Those can be mapped to the SAME HUB RAM bytes allowing for creative screen arrangements using less HUB RAM. It's possible to make little regions too. The techniques are in the commented graphics_demo.spin in my signature.
http://rayslogic.com/Propeller/Products/VgaGraphics/VgaGraphics.htm
For 3 drivers eating up a good amount of COG-RAM this saves up to 6kB.
A quick and dirty solution would be to move the PASM code of all 3 drivers into one big SPIN-file. Having the PASM code in a row allows to reuse this DAT sections a screen-buffer after loading into COGs.
The reason is both the color palette array index number, and the HUB memory address for the pixel data in that tile are encoded into the screen word associated with each tile.
The lower 10 bits for each tile in the screen array point to a 64 byte aligned HUB memory address. That is where the actual pixels are. The upper 6 bits point to an entry in the colors array, and that is where the colors of those pixels are.
You want those to be zero and that equals "colors[0]"
Reading...
In your original post:
You've got it commented out already! All tiles should point to colors[0] now.
This is the piece that does the funky palette assignments:
'+ ((dy & $3F) << 10)
So you should be able to cut the colors array back and have it work. Is that not the case?
Assuming it is, we can move on to shaving some pixels off in the X direction, if you need another 700-1400 bytes.
I also started looking into the WAV code out of curiosity. It's quite a bit over my head, but I see a lot of error handling for unsupported files that I won't need. At a quick swipe at removing the error handling in that code, I freed up 154 longs. I think I am learning some tricks to get where I need to be!
What does your display look like? Do the numbers and such consume all of it, or just some of it? I'm thinking we can take a strip off the X at a minimum. 240 pixels is very similar to the 256 pixels you have now. That's good for 700 bytes, if needed.
I'm going to check out here in a minute, but here is what you do:
First, subtract one from the number of tiles in the X direction:
Then resize your bitmap:
Then in your StartVGA, adjust the graphics setup so that the graphics commands do the right thing in the right place.
You might adjust where you put things. I don't know, didn't check.
Finally, you might want to fiddle with the offsets and scale factors found in:
Sadly, it's been a while since I ran this driver. I think you want the xo, yo parameters to offset the display somewhere acceptable, but I'm not sure. Maybe others are needed too. If I were you, I would draw a big rectangle around the outside edge, see where it all is and fiddle with those, until it's somewhere in the center of your display. Then remove those commands.
Repeat with another strip off the X direction, if you really need to.
If you need more beyond that, we are going to have to exchange info about what your display looks like, or maybe I can run it, or something. If there are blank areas, we can shave off 64 bytes per blank area that is never drawn into, by assigning the tiles in creative ways and then running gr.setup multiple times... Hopefully, it won't go there.
Since my laptop just went to sleep due to the battery, I guess I should call it a night as well. Had to plug it in so I could resume windows to finish typing this post, lol. I will keep working on this tomorrow at lunchtime.
Open all 3 driver files and copy all the code into one new file.
Tweaks needed are:
Rename SPIN-functions and variables which have the same name - probably start and stop. Simply make it startVGA, startGraph & startDAC...
Make sure that you put all PASM DAT sections in a row to give you a continuous block of bytes which then can be reused by VGA as a bitmap- and screen-buffer.
Tweak here is to start VGA as the last driver and to clean the DAT section to avoid seeing too much garbage on the screen.
Being optimistic means that this is all you have to do. It has to be checked weather the graphics driver is fine when started after the VGA driver, but in theory I don't see a reason why not.
Then you use the startcog label mod 64 + 64 as your start of bitmap screen.
But, I see you've already done that. Actually, I see my initials in there on that change...
You could maybe go 4X and further reduce the pixel count, but then it might get too pixelated...
I'd try replacing the wav code with FSRW and my wav player...
Might not have as many features as Kye's code, but I think it's much smaller...
Rayman, You are right. Using your WAV player for 16 bit mono, I will gain another 507 longs. I don't need stereo as long as the mono output to be left and right. The WAV player only needs to be able to play the WAV file. No other options needed at all. (IE: volume, error handling, etc)
If I convert my delays to mS, I can use a single method, so I may do that before my code gets too far out of hand. I also starting noticing that much of my calls to the Graphics object account to less than 10 different types. I can probably convert those to method calls instead of individual code segments to clean up the code structure.
Still have a lot of work to do before Saturday morning. I am sure that I will be out of midnight oil over the next couple nights.
As for the questions about the display, here is what it looks like when at a particular year. There is some black space, but not at the top or bottom edges (my widescreen monitor at my desk makes it appear otherwise however). I think with all my other code condensing, I will not need to cut back the VGA code anymore. BST has made a big difference in compiling for size.
To jump from 2013 to 1978 I call this:
The 300 ms delay between counting years makes it last 12 seconds which finishes at the loud whoosh sound in the sound effect (like coming to a stop in a time machine, lol)