My Apple IIe: A simple text based arcade game in Applesoft Basic

Поділитися
Вставка
  • Опубліковано 15 лис 2024

КОМЕНТАРІ •

  • @Novastar.SaberCombat
    @Novastar.SaberCombat 2 роки тому +2

    I made a "shooting" game like this when I was a kid, but it took waaaaaay more lines of code, and refreshed the screen super slowly (because my code was at an 8 year-old's level in the 80's). It was fun because it used the joystick OR the keyboard for controls.

  • @donc7664
    @donc7664 3 роки тому +2

    Loved it

    • @kurt.leucht
      @kurt.leucht  3 роки тому +1

      Thanks!

    • @donc7664
      @donc7664 3 роки тому +1

      @@kurt.leucht i have a small collection of apple ii's and im always wondering what to do if I ever get them back out and hooked up.

  • @alanr4447a
    @alanr4447a 6 років тому +8

    Additional notes on the "Snails" program:
    The "PRINT CHR$(21)" in line 20 has a special function relating to the "auxiliary video mode". This is the mode invoked when the 80-column text diplay is called up by "PR#3". This mode is also used when the text is switched from 80 to 40 columns via ESC 4. In auxiliary video mode, a number of "control characters" have special actions that they do not have in "simple video mode" (the mode in use when you first turn on the Apple). Some of these can be used to programmatically perform some of the actions carried out by user key sequences such as ESC 4. For example, printing CHR$(17) (commonly known as "ctrl+Q") has the same action as ESC 4, namely, to switch to 40 columns of text within the auxiliary video mode. Printing CHR$(18) ("ctrl+R") switches back to 80 columns, just like ESC 8. As another example, CHR$(25) ("ctrl+Y") "homes" the cursor (puts it in row 1/column 1) WITHOUT clearing the screen. CHR$(21) ("ctrl+U") exits the auxiliary video mode altogether, just like ESC ctrl+Q, returning to simple video mode. It may be confusing that while both invoke 40-column display, ESC ctrl+Q and PRINTing ctrl+Q are not the same thing. The former exits auxiliary video mode, while the latter does not. The program PRINTs the CHR$(21) to insure that auxiliary video mode is exited if it's in use, and that simple video mode is operating - mostly, I imagine, to insure that the display is in 40 column mode, but also just to insure that none of the "auxiliary" features are in play. If the simple video mode is ALREADY operating, CHR$(21) will have no effect, and will not show up as anything (although the PRINT command will end with a "return" generated).
    In line 40, "POKE 49168,0" clears the "keyboard strobe" (causing the PEEK(49152) to stop returning a value greater than 127 for the most recent keystroke, so that the next keystroke will be discernible). This is ONLY necessary when the PEEK(49152) does return a value greater than 127; but the existing program performs this whether or NOT the PEEKed value was indeed greater than 127. This extra action is basically harmless, but totally unnecessary (although there's actually a slim chance that it will indeed cause "harm" by causing a keystroke occurring in the split second between the PEEK and the POKE to be "lost"; for this reason the "extra" POKE 49168 should NOT be performed). In addition, line 30's "GOTO 40" is unnecessary "extra baggage". Both of these shortcomings can be avoided by moving the "POKE 49168,0" from line 40 to line 30 itself with this new line 30:
    30 K = PEEK (49152): IF K > 127 THEN POKE 49168,0:H = (K = 149) - (K = 136) + H
    Now the "POKE 49168,0" is performed ONLY when it is NEEDED, and the "GOTO 40" is dispensed with.
    In (what's left of) line 40, the "IF RND (1) * 10 < 1" is used to flag the random value 10% of the time, or a .1 probability. This could be accomplished without performing a multiplication each time. A multiplication is not a BIG time-consuming factor, but every little bit that can be eliminated helps. The effect of the code is to flag whenever the random value itself is less than 0.1 (which, times 10, would then be less than 1). Instead, the multiplication could be omitted, and simply test against .1 directly. This does, however, raise another issue that the original programmer may have been aware of. When it comes to evaluating numerical literals, it's slower if the literal value is NOT an integer. Integers can be evaluated pretty quickly, but not non-integers. The Applesoft manual describes a solution to this problem under "Speeding up your program" in an Appendix. There it states quite emphatically that it's quicker to retrieve the value of a variable than to evaluate a numerical literal. It fails to acknowledge, however, that this is more true if the literal value is NOT an integer than if it IS one. In any case, the point is taken that with the value .1, it would execute quicker to have that value stored in a variable rather than being expressed literally. To that end, we'll introduce a variable, say "Q", storing the value .1, to be compared to in line 40, rather than the literal ".1". Q will now be assigned the value .1 just once, say in line 20, and line 40 will now read:
    40 IF RND (1) < Q THEN VTAB 20: HTAB RND (1) * 20 + 10: PRINT "@": GOTO 70
    In line 50, the "PRINT CHR$(46)" is used to print a period (which is randomly placed), using CHR$(46) instead of simply "." for some odd reason. "." is a clearer way to express it.
    Examining the program logic in lines 60 and 70, we see that program flow takes two possible paths, depending on "IF PEEK (1535 + H) = 192", both paths then leading to line 80, in one case via "GOTO". Both paths involve the actions "VTAB 5: HTAB H" followed by the printing of (at least) one character. This is ALL that the "IF-false" path does, while the "IF-true" path does more. It would be nice to avoid having the same "VTAB 5: HTAB H" expressed TWICE in the program, as well as to eliminate another "GOTO". The fact that one path is seen as performing the same general action as the other, PLUS additional action on only the one path, lends itself to accomplishing this. What we'll do is replace the simple IF-test of "PEEK (1535 + H) = 192" by re-using the variable K (whose existing use is not needed outside of line 30) to save the result of that evaluation, then carrying out a common "VTAB 5: HTAB H" and the PRINTing of one "formulated" character, followed by an "IF K" to perform the one path's remaining actions right in line 60, and eliminating line 70 altogether. There is an additional complication that line 40 ends with "GOTO 70" directly, without regard to the "PEEK (1535 + H) = 192" evaluation. We will get around that problem by adding "K = 0" to line 40 before the "GOTO". Lines 40 to (now absent) line 70 will now look like this:
    40 IF RND (1) < Q THEN VTAB 20: HTAB RND (1) * 20 + 10: PRINT "@":K = 0: GOTO 60
    50 VTAB 22: HTAB RND (1) * 39 + 1: PRINT ".":K = PEEK (H + 1535) = 192
    60 VTAB 5: HTAB H: PRINT CHR$(86 - K * 51): IF K THEN S = S + 1: VTAB 23:
    PRINT CHR$ (7)"MUNCHED: "S
    "K = 0:" is added to line 40, while its "GOTO" line is changed from 70 to 60. The evaluation of "PEEK (H + 1535) = 192" is stored to K in what is now an additional aspect of line 50. Line 60 has the single occurrence of "VTAB 5: HTAB H" followed by the formualted PRINTing of one character. If "PEEK (H + 1535) = 192" evaluated to true, then K received the value 1; if the test came out false, K received zero. So in the expression "86 - K * 51", a true test gives the final value 35, and a false test gives the value 86. Thus, either a "#" or a "V" is PRINTed accordingly. The following "IF K" then determines whether the remaining actions are carried out (for the situation where a 'snail' got "munched": augmenting the score in S, sounding the "bell"/CHR$(7), and displaying the new score). Then in either case execution continues to line 80. Note that the simple "IF K" becomes an exact substitute for the original "IF PEEK (1535 + H) = 192", as if it were that test still being performed. This is because of what was already explained about the value received by K, plus the notable fact that an IF statement is actually in fact testing whether a numerical expression following the "IF" equals zero or not. The IF is "false" if it equals zero, and "true" for all other values. Relational operators such as "=" (as well as logical operators AND, OR and NOT) always produce a value of 1 if true, and zero if false, which perfectly matches what the "IF" statement is REALLY asking. In fact, ANY time you want to test "whether a variable (such as K) equals zero or not", you can just say "IF K THEN" without any explicit "test value". This is also true of testing whether ANY expression "equals zero or not" (where zero is false, and anything else is true).

    • @kurt.leucht
      @kurt.leucht  4 роки тому

      Wow, thanks!

    • @DennisKovacich
      @DennisKovacich 2 роки тому +2

      That’s an excellent explanation and streamlining of the code! I would only add an explanation of what PEEK (1535 + H) actually does, since the OP didn’t really seem to know. Your “V” player is printed on line 5 in column H, with the columns numbered 1-40 left to right. Memory location 1536 is the first column of line 5, so 1535+H is the spot where you are. PEEKing at that memory location indicates whether a snail (CHR$(192) or “@“) just got scrolled into you, meaning it gets munched.

  • @georgemaragos2378
    @georgemaragos2378 3 роки тому +2

    Hi - pretty cool
    if anyone is having typos or run errors you can do it the easy way if using a emulator
    open notepad
    open your emulator
    if the emulator shows * then press control c to break into basic
    open the listed github page
    copy the code text from github into the notepad in one hit ( all 9 lines )
    now go to the first line of the code in notepad - copy the first line (i use control insert)
    now go to the emulator and paste ( i used shift insert )
    once all is pasted in type list and make sure you are not missing anything if Ok then type run

    • @kurt.leucht
      @kurt.leucht  3 роки тому

      Good advise! I wish there was a shortcut when using the real hardware! So many typos!

  • @alanr4447a
    @alanr4447a 6 років тому +2

    What POKE 35 (in line 20) actually does is set the bottom edge of the "active window" - the region of the screen where print commands normally affect the display. The usual value is 24, meaning all 24 text lines are active. 22 means that the bottom two lines are out of the zone. A big aspect of this is that screen-scrolling will NOT affect the bottom two lines of text, so that those two lines will be preserved while all the lines above that scroll up. Thus, the "Munch count" on line 23 remains while the rest of the screen scrolls. The TEXT in line 90 resets the active window to the entire screen. In PEEK(1535+H) the 1535 refers to the "base address" for the fifth line of text - the memory location of the left edge of the fifth line (the actual left edge is at 1536, but H equals 1 if the V is at the left edge, so that it would evaluate to 1536, the true left edge, in that formula). Greater values of H result in positions farther toward the right being PEEKed (examined). The fifth line is where the bottom of your V formation is positioned, because of the VTAB 5 in line 70.

  • @DrawMakeCode
    @DrawMakeCode 7 років тому +1

    That's a nifty little program. I can see changing the code a little so that one press of the key triggers a continual movement with the player's object passing through the edges to either side. Two presses could trigger the movement to stop in a position. I love how compact 80's programming was. Programmers could do a lot with just a few lines of code.

    • @kurt.leucht
      @kurt.leucht  5 років тому

      Yup! Almost to the point of being completely unreadable and completely incomprehensible! :-)

  • @Ktc99999-b
    @Ktc99999-b 3 роки тому +1

    how do you know what to poke and peek? is there a list?

    • @kurt.leucht
      @kurt.leucht  2 роки тому

      There are several lists on the Internet to choose from. Just search for Applesoft poke and peek commands and you can use your favorite result!

  • @BryonLape
    @BryonLape 5 років тому +3

    There are some nice tricks in there I didn't know could be done in AppleSoft.

    • @kurt.leucht
      @kurt.leucht  4 роки тому

      Yeah, me too. The Beagle Brothers catalog always revealed some fun tricks.

  • @kurt.leucht
    @kurt.leucht  7 років тому +1

    This is a really awesome Apple IIe emulator in case you wanted to play around with your own AppleSoft Basic programming:
    github.com/AppleWin/AppleWin
    Scroll down to the bottom of the site and click on the release zip to download it. No installation is required. Just unzip it and run the executable. Then click the Drive 1 button and select the default (master) file. Then click the apple button to boot.

  • @joseguillen3340
    @joseguillen3340 4 роки тому +1

    How can i save this on floppy

    • @kurt.leucht
      @kurt.leucht  4 роки тому

      Good question. I should cover that in a future video. It's been so long that I can't remember the commands.

    • @kurt.leucht
      @kurt.leucht  4 роки тому

      I think it's LOAD, RUN, and SAVE for BASIC programs and BLOAD, BRUN, and BSAVE for binary executables. So CATALOG to see the files, then LOAD filename will get a program into memory, then RUN will execute it, then edit the code in memory, then SAVE filename to save it back to disk. I think.

  • @RodrigoPerez79
    @RodrigoPerez79 4 роки тому +1

    I copied this and got a "Unsupported PEEK location: 1555 in line 60" message on my apple II emulator.

    • @kurt.leucht
      @kurt.leucht  4 роки тому

      Hmmm. That's the line that checks whether your horizontal location matches a snail, or an @ character. I'm guessing your emulator isn't emulating that particular PEEK. Strange. How much trouble would it be to try it on a different emulator?

    • @kurt.leucht
      @kurt.leucht  4 роки тому

      I wonder if it has something to do with the special auxiliary video mode call in line 20 with the CHR$(21). Maybe that emulator doesn't handle that video mode or something. Now I'm wondering if any of them do.

    • @RodrigoPerez79
      @RodrigoPerez79 4 роки тому +1

      @@kurt.leucht I have a feeling that anyhting that has "peek" wont work on the emulator... This is the one I'm using, btw: www.calormen.com/jsbasic/
      I'll try a different one.

    • @kurt.leucht
      @kurt.leucht  4 роки тому

      @@RodrigoPerez79 Maybe try the one I talk about in this post. It's a standalone executable, rather than javascript inside a web browser. www.leucht.com/blog/2016/09/my-apple-iie-a-simple-text-based-arcade-game-in-applesoft-basic/

    • @RodrigoPerez79
      @RodrigoPerez79 4 роки тому +1

      @@kurt.leucht It worked fine (but a bit slow) on the OpenEmulator

  • @divarin1
    @divarin1 5 років тому

    Thanks for this. I recently picked up an Apple IIc and at the moment have only one disk. While I wait for a null modem cable to get shipped so I can put some software on disks I thought I'd play around in basic. Did you explain how the "stars" get drawn? If so I must have missed that part.

    • @kurt.leucht
      @kurt.leucht  5 років тому

      I did not. Good catch. The "stars" are actually period characters. ASCII character 46. Line 50 prints those.

    • @gregorymalchuk272
      @gregorymalchuk272 5 років тому

      @@kurt.leucht
      Does anybody know if if is possible to scroll up and down, and edit line numbers in the Applesoft BASIC line editor?

    • @kurt.leucht
      @kurt.leucht  5 років тому

      From the command line, the escape key lets you cursor up to a line and modify it, including editing the line number. Although the old lines of code will still be there if you edit the line numbers. So the old lines will need to be deleted.
      See this video for a quick example of escape editing a line:
      ua-cam.com/video/1AiUPVIPd0Y/v-deo.html&t=907

  • @alanr4447a
    @alanr4447a 6 років тому

    Then there's the matter of the play of the game itself. As it is, play continues until you've munched 10 snails, and the game is over. It then reiterates your score and displays how "long" it took you to accomplish this. Of course, as the game is designed, your score is ALWAYS going to be "10", with only the "time" as any kind of variable. I'm not fond of "time" being a scoring consideration. I'd much rather have a game design where your "score" itself is a variable that measures your accomplishment, and isn't ALWAYS going to end up being "10". Instead, I'd rather have the game continue until a certain number of 'snails' have been MISSED, and your score tallies how many you DID "munch" before you reached your miss-limit. This will complicate things, however, as tracking "missed" snails will entail determining every time a snail "escapes" off the top of the screen. Just as the program now tests a location of text-screen memory to see when you've munched a snail, so it will have to "sweep" the top LINE of the text screen to see when any snails "escape". There are 20 positions on the text line where snails may be located; all 20 of these in the top text line will have to be checked to flag escaping snails. A BASIC-loop could be created to check out every position, but this would slow the program (and thus the game) considerably. Instead, I'll create a short machine-language routine to perform this action in a flash! Then I'll add an initial line, with a DATA statement, to the program to store the routine in memory at the outset. Now, I note that the screen gets scrolled up every time a randomly-placed "." is PRINTed. So, I'll CALL the machine-language routine just before this, to count escaping snails. As snails escape, a count of remaining allowable snail-misses will be displayed. When that reaches zero, the game is over, and your count of munched snails is your game score. I'll design it to allow you 25 missed snails; on the 26th missed snail, the game ends. First, here is my machine language routine:
    300: A2 00 LDX #$00 ;Escaping snails will be counted in the X-index; initialize to 0
    302: A0 13 LDY #$13 ;20 screen positions will be examined, at offest 0 to 19 (hex
    $13) - initialize Y with $13
    304: B9 09 04 LDA $0409,Y ;Load an indexed screen byte into A (top line of screen)
    307: C9 C0 CMP #$C0 ;Compare with "@", which equals $C0
    309: D0 01 BNE $030C ;Skip the next step if NOT "@"
    30B: E8 INX ;Increment the snail count in X if it IS "@"
    30C: 88 DEY ;Decrement the horizontal offset in Y
    30D: 10 F5 BPL $0304 ;Loop back to $304 until the offset drops "below zero"
    30F: 86 2C STX $2C ;Store the escaped-snail count to location 44 ($2C)
    311: 60 RTS ;Exit the routine back to Applesoft
    It's a bit of a "trick" in this routine that allows the looping to continue when the "offset" equals zero, and terminate only AFTER that. Countdown looping usually ends AT the reaching of zero, by looping back on "BNE", which means loop back "if not zero" - so that the final loop value is 1. The "trick" can be used with relatively small loop counts (specifically, less than 128) by relying on the fact that all such small values are considered "positive" (more precisely "non-negative"), including zero - byte values in the range 128 to 255 ($80 to $FF) are considered "negative" in BPL testing (specifically where bit 7 of the byte, the MSB, equals 1) - so it's not until Y is decremented FROM zero that the value of Y "wraps around" to the "negative" value 255 ($FF) and fails the BPL test.
    So, with the addition of a new line 10 with a READ/POKE loop and DATA statement, and other additions, the program now looks like this:
    10 FOR U = 768 TO 785: READ K: POKE U,K: NEXT :Q = .1:
    DATA162,0,160,19,185,9,4,201,192,208,1,232,136,16,245,134,44,96
    20 TEXT : HOME :H = 20: PRINT CHR$ (21): POKE 35,22:L = 26
    30 K = PEEK (49152): IF K > 127 THEN POKE 49168,0:H = (K = 149) - (K = 136) + H
    40 IF RND (1) < Q THEN VTAB 20: HTAB RND (1) * 20 + 10: PRINT "@":K = 0: GOTO 60
    50 VTAB 22: HTAB RND (1) * 39 + 1: CALL 768:U = PEEK (44): PRINT ".":
    K = PEEK (H + 1535) = 192: IF U THEN L = (L - U) * (L > U): VTAB 23: HTAB 28:
    PRINT "LEEWAY: "L SPC( 1)
    60 VTAB 5: HTAB H: PRINT CHR$(86 - K * 51): IF K THEN S = S + 1: VTAB 23:
    PRINT CHR$ (7)"MUNCHED: "S
    70 T = T + 1: IF L GOTO 30
    80 TEXT : VTAB 23
    In line 10, new variable U loops the POKE locations from 768 ($300) to 785. K is borrowed to READ the DATA values, which are then POKEd to store the routine. I also initialize Q to .1 in line 10. In line 20, missed snail "leeway" in L is initialized to 26. (The value will first be PRINTed when it drops to 25.) In line 50, before the scroll-inducing "." is PRINTed, the machine-language routine is CALLed ("CALL 768"), and the resulting count in location 44 is PEEKed and stored in U. (Location 44, BTW, is used by ROM routines while performing lores graphics operations; so particularly as this program doesn't do any such thing, that location may be borrowed temporarily for other purposes - and really, it could be borrowed temporarily even if lores operations WERE performed, as those uses are themselves only temporary.) Then at the end of the existing line 50, U is tested, to deal with any missed snails. If there are any, "leeway" value L is reduced by the number of missed snails (L - U) multiplied by the "test question" of whether L is greater than U. This insures that L will never drop BELOW zero to appear as "negative leeway". If L were LESS than U, subtracting U would produce a negative value. In that case, the "false" result of (L > U) would cause (L - U) to be multiplied by zero, forcing an overall result of zero, instead of something negative. Then the program goes to a right-side location of screen line 23 to PRINT the newly-reduced "leeway" value. As this value dropping from a value 10+ to less than 10 would shorten the number PRINTed, "SPC(1)" is used to print one trailing blank. Ending the PRINT with SPC also suppresses the generating of a "return" afterward. This occurs whenever a PRINT statement ends with SPC, TAB, "," or ";", BTW. Line 70 here is essentially a replacement for what WAS line 80 before. "Time"-tracking "T = T + 1" is still there, although nothing else is done with it. It is retained for possible future development. Then the new loop-back test is L being non-zero. The old program-end message is gone; instead, line 80 just performs "TEXT" to cancel the effect of "POKE 35,22" in line 20, and restore the full-screen active window. As it happens, "TEXT" also puts the cursor on line 24. Program termination would place the "]" Applesoft prompt on a new line, causing a screen-scroll. To avoid the scroll, "VTAB 23" puts the cursor on line 23. In addition, throughout the program ";"s were removed from PRINT statements, as they are usually not necessary. The unnecessary "END" was also removed, as the program code comes to an end on its own.

    • @kurt.leucht
      @kurt.leucht  4 роки тому

      Wow! Thanks for that!

    • @alanr4447a
      @alanr4447a 4 роки тому

      @@kurt.leucht You're welcome (for all 3)!

  • @Decco6306
    @Decco6306 5 років тому

    IS it just me or does his monitor slowly wiggle back and forth?
    huh
    I think so

    • @kurt.leucht
      @kurt.leucht  4 роки тому

      It's a very old monitor, so it's entirely possible. But it might also be an aliasing artifact of the scanning CRT not matching the scanning iPhone camera.