Thanks for the replies, but I am trying to optimize more for size than for speed.
Depending on the nature of your problem, you could have pretty optimized solutions.
If you're trying to squeeze your program into a tenliner or alike, the answers above are probably the best.
Now, if you are running out of space in RAM or ROM, you could take advantage of MSX's architecture and the format you plan to have/distribute your program and optimize it in size and, perhaps, speed-wise too!
First, let us establish a baseline.
Suppose the following program to build 16 32x32 sprites:
10 SCREEN2,2:DEFINTA-Z 15 TIME=0 20 DIMA$(15) 100 A$[0]="3C70E0E0E0E0703E1F0E030D0E1E1E3C3C0E070707070E7CB8D0E0F0F078783C" 110 A$[1]="3C70E0E0E0E0703E1F0E030D0E1E1E3C3C0E070707070E7CB8D0E0F0F078783C" 120 A$[2]="3C70E0E0E0E0703E1F0E030D0E1E1E3C3C0E070707070E7CB8D0E0F0F078783C" 130 A$[3]="3C70E0E0E0E0703E1F0E030D0E1E1E3C3C0E070707070E7CB8D0E0F0F078783C" 140 A$[4]="3C70E0E0E0E0703E1F0E030D0E1E1E3C3C0E070707070E7CB8D0E0F0F078783C" 150 A$[5]="3C70E0E0E0E0703E1F0E030D0E1E1E3C3C0E070707070E7CB8D0E0F0F078783C" 160 A$[6]="3C70E0E0E0E0703E1F0E030D0E1E1E3C3C0E070707070E7CB8D0E0F0F078783C" 170 A$[7]="3C70E0E0E0E0703E1F0E030D0E1E1E3C3C0E070707070E7CB8D0E0F0F078783C" 180 A$[8]="3C70E0E0E0E0703E1F0E030D0E1E1E3C3C0E070707070E7CB8D0E0F0F078783C" 190 A$[9]="3C70E0E0E0E0703E1F0E030D0E1E1E3C3C0E070707070E7CB8D0E0F0F078783C" 200 A$[10]="3C70E0E0E0E0703E1F0E030D0E1E1E3C3C0E070707070E7CB8D0E0F0F078783C" 210 A$[11]="3C70E0E0E0E0703E1F0E030D0E1E1E3C3C0E070707070E7CB8D0E0F0F078783C" 220 A$[12]="3C70E0E0E0E0703E1F0E030D0E1E1E3C3C0E070707070E7CB8D0E0F0F078783C" 230 A$[13]="3C70E0E0E0E0703E1F0E030D0E1E1E3C3C0E070707070E7CB8D0E0F0F078783C" 240 A$[14]="3C70E0E0E0E0703E1F0E030D0E1E1E3C3C0E070707070E7CB8D0E0F0F078783C" 250 A$[15]="3C70E0E0E0E0703E1F0E030D0E1E1E3C3C0E070707070E7CB8D0E0F0F078783C" 300 FORS=0TO15:B$="":FORI=1TO31STEP2:B$=B$+CHR$(val("&H"+MID$(a$[s],I,2))):NEXT:SPRITE$(0)=B$:NEXT 500 A=TIME 510 SCREEN 0 520 PRINT A
The sprite routine (data+code, lines 20-300) takes 1390 bytes in program space, some more space in variable/string space and runs in 166 frames in an MSX2+ 60Hz machine in WebMSX.
The whole program takes 186 frames to run if executed with
time=0:run"a.bas"
Now, if you're embedding you MSXBASIC in a cartridge image (ROM file), you could insert your 32x16=512 sprite bytes somewhere in you rom file and simplify your program to (supposing sprite data in &hBE00-&hBFFF):
10 SCREEN2,2:DEFINTA-Z 15 TIME=0 100 FORI=0TO511:VPOKEBASE(9)+I,PEEK(&hBE00+i):NEXT 500 A=TIME 510 SCREEN 0 520 PRINT A
Now your sprite "routine" is just line 100, and is 43 bytes long. With the 512 bytes of data, it consumes 555 bytes of your ROM, instead of the original 1390 bytes, and no space in string area. The execution, however, jumps from 166 frames to 290 (VPOKE is a slow instruction) and the whole program executed with
time=0:run"a.bas"
takes 303 frames, instead of the original 186. A simple routine in assembler well under 100 bytes could solve that speed problem.
Finally, if you plan to have you program distributed as a BAS file in a disk, you could have all your sprite data in a VRAM dump file, previously saved with
BSAVE"SPRITE.SC2",base(9),base(9)+16*32-1,S
Following that approach, your BASIC program now becomes:
10 SCREEN2,2:DEFINTA-Z 15 TIME=0 100 BLOAD"SPRITE.SC2",S 500 A=TIME 510 SCREEN 0 520 PRINT A
Again your sprite "routine" is just line 100, and it takes only 17 bytes of RAM, instead of the original 1390 bytes, and no space in variable area.
The execution of the BLOAD takes only 6 frames, in opposition to the original 166 frames, and the whole program executed with
time=0:run"a.bas"
takes 19 frames, instead of 186.
Note: you can use this approach if you intend to use cassete tape as well. Just record your sprite file after your basic program.
Hope that helps.
Like I said before, I think using the same variable takes less memory, and the speed doesn't have to change much.
10 SCREEN2,2:DEFINTA-Z 15 TIME=0: S=0 100 A$="1111111111111111111111111111111111111111111111111111111111111111": GOSUB500 110 A$="2222222222222222222222222222222222222222222222222222222222222222": GOSUB500 120 A$="3333333333333333333333333333333333333333333333333333333333333333": GOSUB500 130 A$="4444444444444444444444444444444444444444444444444444444444444444": GOSUB500 140 A$="5555555555555555555555555555555555555555555555555555555555555555": GOSUB500 150 A$="6666666666666666666666666666666666666666666666666666666666666666": GOSUB500 160 A$="7777777777777777777777777777777777777777777777777777777777777777": GOSUB500 170 A$="8888888888888888888888888888888888888888888888888888888888888888": GOSUB500 180 A$="9999999999999999999999999999999999999999999999999999999999999999": GOSUB500 190 A$="3C70E0E0E0E0703E1F0E030D0E1E1E3C3C0E070707070E7CB8D0E0F0F078783C": GOSUB500 300 S=TIME 400 SCREEN0: PRINT S: END 500 B$="":FORI=1TO31STEP2:B$=B$+CHR$(val("&H"+MID$(a$,I,2))): NEXT I: SPRITE$(S)=B$: S=S+1: RETURN
Otherwise, BLOAD is the best solution when the listing is not needed.
Like I said before, I think using the same variable takes less memory.
You are right. This uses 500 bytes less than my baseline and runs in 98 cycles, instead of 166 - probably due to the lack of array indexing.
Now, an interesting effect is that it should not take less string area. MSXBASIC does create indexes for every variable in the array, but it does not copy the content to string area in this case. It rather points the variable to the constant in the source code. It is pretty smart and efficient!