Showing posts with label single. Show all posts
Showing posts with label single. Show all posts

Friday, December 27, 2013

Petit Computer Journal #31



Digital Koto

A while back, I wrote a little piano program. It includes song player. It's just a little simple music player, so I used BEEP instead of MML command. However, I'm rather curious since most of the code is created for the interface. In other words, drawing the piano keys. So, I'm wondering whether or not it's simpler to just draw the keys straight. Well, what kind of musical instrument is like that? The answer is simple: Japanese Koto. In truth, different cultures have different ideas and names for what basically amounts to a bunch of strings mounted on a board.

Still, the idea of it is interesting, and it's simple enough for me to do. So, let' do it! There are several different parts, but generally speaking, once you touch the screen, you come up with a string number and beep the appropriate sound. That's just look up table. Touch screen is easy. About the only thing that's difficult is drawing the strings proportionately. A little mathematic should do it.


  1. INIT
  2. Build BEEP table
  3. Build Note table
  4. Build String table
  5. LOOP
  6. Draw Strings
  7. If Touch screen
  8. Highlight nearest string
  9. Play BEEP sound
  10. GOTO LOOP


So, do you think this will take you one hour to do? Time for another "Hour of Code" challenge!


  1. ACLS:CLEAR
  2. DIM BP[49]:'BEEP TABLE
  3. DIM NT$[49]:'NOTE TABLE
  4. DIM ST[16]:'STRING TABLE
  5. BREPEAT 0,60,12
  6. BREPEAT 1,60,12
  7. BREPEAT 2,60,12
  8. BREPEAT 3,60,12

  9. @INIT
  10. NSI=0
  11. P=4096/12:T$="CcDdEFfGgAaB"
  12. FOR I=-24 TO 24:J=I+24
  13. BP[J]=P*I
  14. NT$[J]=MID$(T$,(J%12),1)
  15. NEXT
  16. ST[0]=14:ST[1]=19
  17. ST[2]=21:ST[3]=22
  18. ST[4]=26:ST[5]=27
  19. ST[6]=31:ST[7]=33
  20. ST[8]=34:ST[9]=38
  21. ST[10]=39:ST[11]=43
  22. ST[12]=45:ST[13]=0
  23. ST[14]=0:ST[15]=0

  24. ACLS


That's about it for the Initialization table. I want to fiddle with the string, so I'll put in button interface so I can change the string values on the fly.


  1. @LOOP
  2. NS=13:A=256/NS:B=A/2
  3. VSYNC 1:BT=BTRIG():TX=FLOOR(TCHX*NS/256)
  4. IF NS<1 THEN NS=1
  5. FOR I=0 TO NS-1
  6. GLINE B+I*A,16,B+I*A,192,15
  7. LOCATE (B+I*A)/8,0:?FLOOR(ST[I]/12)
  8. LOCATE (B+I*A)/8,0:?NT$[ST[I]]
  9. NEXT
  10. GLINE B+TX*A,16,B+TX*A,192,8
  11. IF TCHTIME>0 THEN BEEP 20,BP[ST[TX]]
  12. GOTO @LOOP


And that's about it. 35 minutes so far, and that's because I was having trouble with the math. The truth is, had I did the math correctly beforehand, I wouldn't have had so much trouble and I wouldn't have spend the time debugging.

Oh, yeah, about the tuning on the fly. I think that it is nice to be able to change the notes. So, what I'll do is add some button interface. That's what the BREPEAT command is for. Also, I may as well throw the instrument along with it. Instead of the locked in BEEP 20, I substitute it with a variable that will allow you to choose your own instrument.

Finally, we're going to move the whole thing down to the lower screen for ease of play. What should we put on the top of the screen? I don't know. Title screen, maybe? Or some internal variables?


  1. REM DIGITAL KOTO
  2. REM BY HARRY HARDJONO
  3. REM DEC 2013

  4. ACLS:CLEAR
  5. DIM BP[49]:'BEEP TABLE
  6. DIM NT$[49]:'NOTE TABLE
  7. DIM ST[16]:'STRING TABLE
  8. BREPEAT 0,60,12
  9. BREPEAT 1,60,12
  10. BREPEAT 2,60,12
  11. BREPEAT 3,60,12
  12. PNLTYPE "OFF"

  13. @INIT
  14. NSI=0
  15. CI=16:'CURRENT INSTRUMENT
  16. P=4096/12:T$="CcDdEFfGgAaB"
  17. FOR I=-24 TO 24:J=I+24
  18. BP[J]=P*I
  19. NT$[J]=MID$(T$,(J%12),1)
  20. NEXT
  21. ST[0]=14:ST[1]=19
  22. ST[2]=21:ST[3]=22
  23. ST[4]=26:ST[5]=27
  24. ST[6]=31:ST[7]=33
  25. ST[8]=34:ST[9]=38
  26. ST[10]=39:ST[11]=43
  27. ST[12]=45:ST[13]=0
  28. ST[14]=0:ST[15]=0

  29. GPAGE 1:ACLS

  30. @LOOP
  31. NS=13:A=256/NS:B=A/2
  32. VSYNC 1:BT=BTRIG():TX=FLOOR(TCHX*NS/256)
  33. IF NS<1 THEN NS=1
  34. GPAGE 1,1,1
  35. FOR I=0 TO NS-1
  36. GLINE B+I*A,16,B+I*A,192,15
  37. LOCATE (B+I*A)/8,0:?FLOOR(ST[I]/12)
  38. LOCATE (B+I*A)/8,0:?NT$[ST[I]]
  39. NEXT
  40. GLINE B+TX*A,16,B+TX*A,192,8
  41. IF TCHTIME>0 THEN BEEP CI,BP[ST[TX]]

  42. IF BT AND 1 THEN ST[NSI]=ST[NSI]+1
  43. IF BT AND 2 THEN ST[NSI]=ST[NSI]-1
  44. IF BT AND 4 THEN NSI=NSI-1
  45. IF BT AND 8 THEN NSI=NSI+1
  46. IF BT AND 16 THEN CI=CI+1:GOSUB @DISP
  47. IF BT AND 32 THEN CI=CI-1:GOSUB @DISP
  48. NSI=(NSI+NS)%NS
  49. ST[NSI]=(ST[NSI]+49)%49
  50. CI=(CI+70)%70
  51. GOTO @LOOP

  52. @DISP
  53. ACLS
  54. ?"INST: ";CI
  55. FOR I=0 TO 15
  56. ?I,ST[I]
  57. NEXT
  58. RETURN



Oh, by the way. The actual problem is so simple, that you can actually fit the minimalistic version of the program into one screen. That's right. It's another single screen challenge program!


  1. REM DIGITAL KOTO
  2. REM BY HARRY HARDJONO
  3. ACLS:CLEAR:DIM BP[49]
  4. DIM NT$[49]:DIM ST[16]
  5. P=4096/12:T$="CcDdEFfGgAaB"
  6. FOR I=-24 TO 24:J=I+24
  7. BP[J]=P*I
  8. NT$[J]=MID$(T$,(J%12),1):NEXT
  9. ST[0]=14:ST[1]=19:ST[2]=21
  10. ST[3]=22:ST[4]=26:ST[5]=27
  11. ST[6]=31:ST[7]=33:ST[8]=34
  12. ST[9]=38:ST[10]=39:ST[11]=43
  13. ST[12]=45:ST[13]=0:ST[14]=0
  14. @LOOP
  15. NS=13:A=256/NS:B=A/2
  16. VSYNC 1:TX=FLOOR(TCHX*NS/256)
  17. GPAGE 1,1,1:FOR I=0 TO NS-1
  18. GLINE B+I*A,0,B+I*A,192,15
  19. NEXT
  20. GLINE B+TX*A,16,B+TX*A,192,8
  21. SD=BP[ST[TX]]
  22. IF TCHTIME>0 THEN BEEP 16,SD
  23. GOTO @LOOP


Friday, November 15, 2013

Petit Computer Journal #25


Background Maker

A while back, I did a BGMaker program. That's a program that takes in graphic screen (GRP0) and turn it into background (SCU0) and background tiles (BGU0, BGU1, BGU2,BGU3). I had suspected it that it fits into one screen, and it did. Here is my entry to the single screen programming challenge:


  1. 'BGMAKER BY HARRY HARDJONO
  2. CLS:CLEAR:DIM TL$[1024]:MT=-1
  3. OX=0:OY=0:RX=31:RY=23:P$="G2"
  4. ?"GRAPHIC?":INPUT P$
  5. LOAD "GRP0:"+P$,FALSE
  6. FOR Y=0 TO RY:FOR X=0 TO RX
  7. X1=X*8:X2=X1+7:Y1=Y*8:Y2=Y1+7
  8. G$="":FOR YY=Y1 TO Y2
  9. FOR XX=X1 TO X2
  10. G$=G$+HEX$(GSPOIT(XX,YY)%16)
  11. NEXT:NEXT
  12. CT=-1:FOR I=MT TO 0 STEP -1
  13. IF G$==TL$[I] THEN CT=I
  14. NEXT
  15. IF CT==-1 THEN GOSUB @SETCHR
  16. BGPUT 0,OX+X,OY+Y,CT,0,0,0
  17. NEXT:NEXT:SAVE "SCU0:T0"
  18. FOR I=0 TO FLOOR(MT/256)
  19. B$="BGU"+STR$(I)
  20. SAVE B$+":B"+STR$(I):NEXT:END
  21. @SETCHR
  22. MT=MT+1:TL$[MT]=G$:CT=MT
  23. BU$="BGU"+STR$(FLOOR(MT/256))
  24. CHRSET BU$,MT%256,G$:RETURN


I also did a version with no memory. I'm not going to show it here because it's not only complicated, but also very, very slow. In fact, at one point, I actually put in an array to do it. That's when I decided to do a version with arrays instead.

Anyway, if you look at the code above, you'll see that it only works on single graphic screen. That's not what I want to have. I want to have the full 64 by 64 background tile generator. As you can see, from the variables OX, OY, P$, even though they do nothing in this version, the original design does take the full background field into account.

Here is the updated BGMAKER program that includes all available background screen. You'll need six graphic screens. They're all loaded into GRP0, but the build is done on different parts of BG screen.

'BGMAKER BY HARRY HARDJONO
CLS:CLEAR:DIM TL$[1024]:MT=-1
@PICFILE
DATA "G1","G2","G3","G6","G5","G4"
RESTORE @PICFILE

All this does is some initialization. I put the file names into DATA statements because I'm too lazy to modify the code. So I simply replaced all INPUT P$ into READ P$.


  1. 'LOAD GRPS
  2. OX=0:OY=0:RX=31:RY=23:READ P$
  3. LOAD "GRP0:"+P$,FALSE
  4. BGOFS 0,OX*8,OY*8,60:GOSUB @TILER


Okay, you can see the pattern here. OX, OY are relative shifting or original X,Y coordinate. RX,RY are the size or width and height of the screen. I mentioned changing INPUT P$ into READ P$. Once I loaded the graphic into screen 0, I shifted the background to the appropriate place and call the @TILER subroutine. Since OX,OY are zeros, I guess I don't need to shift the background (using BGOFS), but I'm doing it here for consistency.


  1. OX=32:OY=0:RX=31:RY=23:READ P$
  2. LOAD "GRP0:"+P$,FALSE
  3. BGOFS 0,OX*8,OY*8,60:GOSUB @TILER

  4. OX=0:OY=24:RX=31:RY=23:READ P$
  5. LOAD "GRP0:"+P$,FALSE
  6. BGOFS 0,OX*8,OY*8,60:GOSUB @TILER

  7. OX=32:OY=24:RX=31:RY=23:READ P$
  8. LOAD "GRP0:"+P$,FALSE
  9. BGOFS 0,OX*8,OY*8,60:GOSUB @TILER

  10. OX=0:OY=48:RX=31:RY=15:READ P$
  11. LOAD "GRP0:"+P$,FALSE
  12. BGOFS 0,OX*8,OY*8,60:GOSUB @TILER

  13. OX=32:OY=48:RX=31:RY=15:READ P$
  14. LOAD "GRP0:"+P$,FALSE
  15. BGOFS 0,OX*8,OY*8,60:GOSUB @TILER


As you can see, only the first line is being changed. I could have just put the whole thing into a subroutine. However, I'm not doing it because of the possibility that I don't have the whole 6 graphics to play with. If I only have four graphic screens, for example, it's easy for me to just comment out the necessary lines. This is judgement call, so you can certainly do it different. Whatever works.


  1. @SAVEBG
  2. SAVE "SCU0:T0"
  3. FOR I=0 TO FLOOR(MT/256)
  4. B$="BGU"+STR$(I)
  5. SAVE B$+":B"+STR$(I)
  6. NEXT 
  7. WAIT 300


Even though this is labelled as a subroutine. This is just a piece of code. It's not being called by GOSUB. I simply put the label there because I don't need to use comment. I could have, but simply decided not to.


  1. FOR I=0 TO 99
  2. X=RND(512):Y=RND(512)
  3. BGOFS 0,X,Y,60:WAIT 60
  4. NEXT
  5. WAIT 300


This is simply the scrolling of the background. You see 60 at the end of BGOFS call. This is equivalent of one second. So, I scroll for one second, while waiting for one second. What happens if I don't wait? Then the scroll will be reset immediately.


  1. BGCLR
  2. CHRINIT "BGU0"
  3. CHRINIT "BGU1"
  4. CHRINIT "BGU2"
  5. CHRINIT "BGU3"
  6. GCLS

  7. END


This code is necessary to reset the states to the original state. Otherwise, you will see that your screen is overlaid with your background. Of course, during debugging, that's quite problematic for me, since oftentimes the program does not run to completion, so I end up doing these commands by hand.


  1. @TILER
  2. FOR Y=0 TO RY:FOR X=0 TO RX
  3. X1=X*8:X2=X1+7:Y1=Y*8:Y2=Y1+7
  4. GOSUB @TILESTR
  5. CT=-1:FOR I=MT TO 0 STEP -1
  6. IF G$==TL$[I] THEN CT=I
  7. NEXT
  8. IF CT==-1 THEN GOSUB @SETCHR
  9. BGPUT 0,OX+X,OY+Y,CT,0,0,0
  10. NEXT:NEXT
  11. RETURN


There's some confusion here. I should have put the X1,X2,Y1,Y2 line into @TILESTR as it serves no purpose here. TL$[I] is the 64 hexadecimal string that contains the tile info. What the program does is simply set CT (Current Tile) to -1. Whenever the tile is found, CT is set to I. If the tile is not found, then CT will contain the value of -1. Then we know to store the current string (G$) into the tile array. After that, it's trivial to just set CT into the background.

Notice that I'm not doing mirror comparison. The documentation mentioned X,Y rotation. I'm ignoring that here. Actually, those aren't X,Y rotation. Those are mirror flip. If you want to you may want to modify the code to include that check. It's easy enough to do.


  1. @TILESTR
  2. G$=""
  3. FOR YY=Y1 TO Y2
  4. FOR XX=X1 TO X2
  5. G$=G$+HEX$(GSPOIT(XX,YY)%16)
  6. NEXT:NEXT
  7. RETURN


I have to make a decision here. Graphic screens can handle 256 colors. BG tiles can only handle 16 colors. What do I do? Well, if you notice, BG colors can actually have 240 colors. 256 colors-16 transparent colors. You can differentiate the different palettes, but I simply decided not to.

If you want to differentiate the palettes, then you want to store FLOOR(GSPOIT(XX,YY)/16) value and enter it into G$ so that it doesn't match equivalent pattern but different palette.

Another factor to consider is the X,Y mirror. Now you see why there's X1,X2,Y1,Y2. This is done for ease of mirroring. Normal: Y1-Y2/X1-X2. X Mirror: Y1-Y2/X2-X1. Y Mirror: Y2-Y1/X1-X2. XY Mirror: Y2-Y1/X2-X1. Note that I haven't done so, so the above is not guaranteed to work. Making it work is left as homework for the readers. ;)


  1. @SETCHR
  2. MT=MT+1:IF MT>1023 THEN MT=1023
  3. TL$[MT]=G$:CT=MT
  4. BU$="BGU"+STR$(FLOOR(MT/256))
  5. CHRSET BU$,MT%256,G$
  6. RETURN


Nothing to it. This simply increment the MT (MaxTile) counter and set the character to the appropriate BGUX bank.

And there you go. A very easy to do Background Maker. Here is the whole completed source code for your convenience. I suppose I should do the QR. Well, maybe sometimes in the future, or maybe not. Easy enough for you to do it by hand.



  1. 'BGMAKER BY HARRY HARDJONO
  2. CLS:CLEAR:DIM TL$[1024]:MT=-1
  3. @PICFILE
  4. DATA "G1","G2","G3","G6","G5","G4"
  5. RESTORE @PICFILE

  6. 'LOAD GRPS
  7. OX=0:OY=0:RX=31:RY=23:READ P$
  8. LOAD "GRP0:"+P$,FALSE
  9. BGOFS 0,OX*8,OY*8,60:GOSUB @TILER
  10. OX=32:OY=0:RX=31:RY=23:READ P$
  11. LOAD "GRP0:"+P$,FALSE
  12. BGOFS 0,OX*8,OY*8,60:GOSUB @TILER
  13. OX=0:OY=24:RX=31:RY=23:READ P$
  14. LOAD "GRP0:"+P$,FALSE
  15. BGOFS 0,OX*8,OY*8,60:GOSUB @TILER
  16. OX=32:OY=24:RX=31:RY=23:READ P$
  17. LOAD "GRP0:"+P$,FALSE
  18. BGOFS 0,OX*8,OY*8,60:GOSUB @TILER
  19. OX=0:OY=48:RX=31:RY=15:READ P$
  20. LOAD "GRP0:"+P$,FALSE
  21. BGOFS 0,OX*8,OY*8,60:GOSUB @TILER
  22. OX=32:OY=48:RX=31:RY=15:READ P$
  23. LOAD "GRP0:"+P$,FALSE
  24. BGOFS 0,OX*8,OY*8,60:GOSUB @TILER

  25. @SAVEBG
  26. SAVE "SCU0:T0"
  27. FOR I=0 TO FLOOR(MT/256)
  28. B$="BGU"+STR$(I)
  29. SAVE B$+":B"+STR$(I)
  30. NEXT 
  31. WAIT 300

  32. FOR I=0 TO 99
  33. X=RND(512):Y=RND(512)
  34. BGOFS 0,X,Y,60:WAIT 60
  35. NEXT
  36. WAIT 300

  37. BGCLR
  38. CHRINIT "BGU0"
  39. CHRINIT "BGU1"
  40. CHRINIT "BGU2"
  41. CHRINIT "BGU3"
  42. GCLS

  43. END

  44. @TILER
  45. FOR Y=0 TO RY:FOR X=0 TO RX
  46. X1=X*8:X2=X1+7:Y1=Y*8:Y2=Y1+7
  47. GOSUB @TILESTR
  48. CT=-1:FOR I=MT TO 0 STEP -1
  49. IF G$==TL$[I] THEN CT=I
  50. NEXT
  51. IF CT==-1 THEN GOSUB @SETCHR
  52. BGPUT 0,OX+X,OY+Y,CT,0,0,0
  53. NEXT:NEXT
  54. RETURN

  55. @TILESTR
  56. G$=""
  57. FOR YY=Y1 TO Y2
  58. FOR XX=X1 TO X2
  59. G$=G$+HEX$(GSPOIT(XX,YY)%16)
  60. NEXT:NEXT
  61. RETURN

  62. @SETCHR
  63. MT=MT+1:IF MT>1023 THEN MT=1023
  64. TL$[MT]=G$:CT=MT
  65. BU$="BGU"+STR$(FLOOR(MT/256))
  66. CHRSET BU$,MT%256,G$
  67. RETURN