Hey guys,
Maybe some smart folks can help me out. I'm at a loss how to do two things, the first in ML, the second in BASIC 2.0.
1) I'd like to POP GOSUBs off the stack. I know where the stack resides, but how would you do it? I've searched the Internet and c.s.c., and come up empty. Short of using flags that dictate a GOTO vs. RETURN (which is a mess in my code) ...
2) I'm not sure how to phrase this gracefully. How would one address one particular bit in BASIC if I had a region of 30 bytes in a string [chr$(255) equal to all bits set]?
Say I had this group of 4 bytes, showing individual bits:
00000000 00000100 00000000 00000000
and I wanted to toggle bit 14 back to a zero. What is the algorithm used? I suck and AND and OR and Boolean logic.
Something like POKE <varptr>,(byte and 8) or 4...? Seems like I've seen something like this used before if you wanted to flip a pixel on or off in a hi-res screen by hand, but I can't find the algorithm. Of course, the bitmap is not linear, but rather top-to-bottom as if each 8x8 "cell" were a "text mode" reprogrammable character...
It just all makes my head ache. Trying to make progress on my adventure game. :)
By the way, Gene Buckle put up the game "The Land of Spur", running on an Apple //e, available on telnet at:
aor.retroarchive.org
This game is what Totally Awesome Dungeon Adventure is based off of. Contrary to what I may have made it sound like, TADA is not original, I'm just porting TLOS over and adding a few features along the way.
Um... anyway.
Thanks for any help/insight anyone might be able to provide.
~ Pina ~
How easy would it be to adapt a POP routine from the PET?
QuotePET/CBM POP
Michael W.Schaffer
You can avoid stacking up too many subroutines by using POP to cancel a GOSUB (command that sends control to a subroutine at a given line number and then RETURNs to the statement after GOSUB). A programming tool for all PET/CBM computers.
Atari BASIC and the Microsoft BASIC used on the Apple II provide a rather useful command called POP. The POP command removes the last GOSUB from the stack, so that a RETURN will return the program to the second-to-last GOSUB. For example, in this program:
10 GOSUB100
20 PRINT "CONTROL RETURNS HERE."
30 STOP
100 GOSUB200
110 PRINT "NOT HERE."
120 STOP
200 POP
210 PRINT "GOING"
220 RETURN
the RETURN on line 220 returns the program to line 20 (not 110). This utility can be very useful, but it is not available in Commodore BASIC. Well, it wasn't.
Here is a machine language utility that executes a POP on all PET/CBM models. The code is position independent – in other words, it can be moved to any convenient spot in memory without any changes. I prefer to locate the code at the top of memory. A POKE 53,127:POKE 52,0:CLR (for 32K systems) will prevent BASIC from using this space.
Program 1 provides the machine language routine in the form of a BASIC loader. The program will load and protect the POP routine, and then indicate the proper SYS location to call the routine. Programs 2 and 3 provide changes for older ROMs.
A GOSUB in BASIC pushes five bytes onto the system stack. These bytes tell BASIC where to start running when the RETURN statement is executed. These five bytes are the low and high bytes of the CHRGET pointer (locations 119 and 120 for newer ROMs, 221 and 222 for Original ROMs) and the current line number (locations 54 and 55 for newer ROMs, 136 and 137 for Original ROMs), and the token for GOSUB (141). To perform a POP, all we do is remove these five bytes from the stack. The routine uses the same subroutine that BASIC uses (JSR $B322 for BASIC 4.0, JSR $C2AA for Upgrade BASIC, JSR $C2AC for Original BASIC) to search the stack for the GOSUB token. The subroutine loads the accumulator with the token found at the top of the stack. We compare it to 141 to see if we have located a GOSUB. If a GOSUB is not found, then an error is returned. The error message sent is "?without gosub error in xxxx". Notice that the standard BASIC error routine is used, so program and variable integrity are assured. The five PLAs simulate the action of a RETURN without really doing anything.
This utility is especially useful in highly "modular" programs. An error handling subroutine can easily remove "pending" GOSUBs from the stack to prevent them from building up (and resulting in an "?out of memory error").
To use this POP in the preceding program, change the POP in line 200 to a SYS 32512, or whatever SYS location the loader indicates should be used. The program does not change in any other way.
Program 1: BASIC 4.0 Version
10 POKE53, PEEK (53) - 1 : POKE 52, 0: CLR
20 SADR = PEEK (52) + PEEK (53) * 256
30 FOR ADDR = SADR TO SADR + 22
40 READ DTTA : POKE ADDR, DTTA : NEXT ADDR
50 PRINT "USE SYS "; SADR
60 END
70 DATA 169, 255, 133, 71, 32, 34, 179, 201
80 DATA 141, 240, 5, 162, 29, 76, 207, 179
90 DATA 154, 104, 104, 104, 104, 104, 96
Program 2: Make These Changes For Upgrade BASIC
70 DATA 169, 255, 133, 71, 32, 170, 194, 201
80 DATA 141, 240, 5, 162, 29, 76, 87, 195
Program 3: Make These Changes For Original BASIC
70 DATA 169, 255, 133, 71, 32, 172, 194, 201
80 DATA 141, 240, 5, 162, 29, 76, 89, 195
The gosub issue is a matter of moving the stack pointer, but Id be cautious there for obvious reasons. If it does work, I think reliability would be a concern. Perhaps a better option would be calculated goto's. (theres code to do that - see April 1988 Gazette : http://www.commodore128.org/gaztype.html)
The other is about knowing what to AND and OR. I may be speaking the obvious to you, but here's a quick rundown:
101001
AND 000001
-----------
000001
So AND-ing something is used to confirm if a single bit is set. The test above, I AND it with the bit I want to look for. If the answer is 1 (or another value depending on the bits you are testing), then it is true. This translates to alot of IF PEEK(XXXX) AND 128 (or whatever) THEN...
IF PEEK(XXX) AND 1 = 1 (Test if bit 0 is on - right-most bit)
IF PEEK(XXX) AND 2 = 2 (Test if bit 1 is on)
IF PEEK(XXX) AND 4 = 4 (Test if bit 2 is on)
IF PEEK(XXX) AND 8 = 8 (Test if bit 3 is on)
IF PEEK(XXX) AND 16 = 16 (Test if bit 4 is on)
IF PEEK(XXX) AND 32 = 32 (Test if bit 5 is on)
IF PEEK(XXX) AND 64 = 64 (Test if bit 6 is on)
IF PEEK(XXX) AND 128 = 128 (Test if bit 7 is on - left-most bit)
You can combine tests, by adding their position...
IF PEEK(XXX) AND 3 = 3 (Test if bit 0 and bit 1 is on)
As for OR...
111110
OR 111111
------------
111111 ORing a value turns a bit on (here we turned on all bits)
Which is why you see alot of POKE XXXX, PEEK(XXXX) OR 255 (or whatever). Just an easy way to turn on a single bit, or multiple bits
Xlar,
Thanks for the quick reply! Yeah, I know that messing with the stack pointer isn't the best thing to do... unfortunately the game I'm trying to port relies on POP (it's a scripted language)... *counts* 91 times. :/
I do know about the TSX and TXS opcodes...
It never was terribly obvious to me when people start throwing around AND and OR logic, so thanks for the table.
Hmm, download to the disk image is broken.
Not that I plan to do any programming with TADA soon, but probably going to do some plotting/code testing.
Once again, thanks!
Hey man,
I found this from the Ahoy May 87 issue:
1 REM C64 version
2 FOR J = 679 TO 679+22:READA:POKEJ,A:NEXTJ:END
3 DATA 104,104,169,255,133,74,32,138,163,154,201,141
4 DATA 240,3,76,224,168,104,104,104,104,104,96
1 REM C128 version
2 FOR J = 4864 TO 4864+23:READA:POKEJ,A:NEXTJ:END
3 DATA 104,104,104,104,169,141,32,170,79,240,5,162
4 DATA 12,76,60,77,32,80,80,160,5,76,89,80
To demonstrate how to get out of requiring a RETURN statement, after you run one of the above, try this:
10 GOSUB 20
20 A=A+1:PRINT A:SYS 679:GOTO 10
(substitute 4864 if using a 128). What youll find instead of an out of memory error, is that the need for a RETURN has been removed from the stack.
Well that works just great! Thanks so much, xlar!
Now to disassemble it and figure out how the magic works. ;P
Bump. :)
Well, done some more programming on TADA lately, mainly reorganizing disk images and such. Trying to get rid of all the duplicated old code and such, too. Fixing a few bugs here and there, also. Yay me. :)
Here is some code I've been trying to figure out why it works (or doesn't work) the way it does.
; "wedge.s"
; written 12/5/2008
; pinacolada's attempt to write a basic
; wedge, using & character like image
; bbs does.
chrget = $73
chrgot = chrget+6
ierror = $0300
imain = $0302
igone = $0308
*= $c000
c000 ad 08 03 setup lda igone
c003 18 clc
c004 69 03 adc #3
c006 8d 2d c0 sta oldigone
c009 ad 09 03 lda igone+1
c00c 69 00 adc #0
c00e 8d 2e c0 sta oldigone+1
c011 a9 1c lda #<newwedge
c013 8d 08 03 sta igone
c016 a9 c0 lda #>newwedge
c018 8d 09 03 sta igone+1
c01b 60 rts
c01c 20 73 00 newwedge jsr chrget
c01f c9 26 cmp #"&" ; wedge token
c021 f0 06 beq newbasic
c023 20 79 00 jsr chrgot
c026 6c 2d c0 jmp (oldigone)
c029 ee 20 d0 newbasic inc $d020
c02c 60 rts
; variables
c02d 00 00 oldigone .byte $00,$00
c02f 00 00 newigone .byte $00,$00
This is copied from Overlord's ML, and I'm just trying to learn stuff about wedges and all sorts of annoying things which are giving me fits.
When I assemble it, it assembles and SYS 49152 works. But the first use of & will lock up the computer. I have to reset it and do another SYS 49152.
Afterwards, & will indeed increment the border color, when used by itself. But put it in a line like this:
0 &:for x=1 to 500:next:goto
it executes what looks like a warm start and sits there at the READY. prompt. :/
So then I thought, maybe inserting SEI before the LDA IGONE and SEI before the RTS (both in the SETUP area) would help. No dice.
Any thoughts?
Glad to see you're still working on TADA! I am still overwhelmed by all the stuff in the last download (that I got like a year ago).
To answer your most recent question (about your wedge code), the problem is you should not RTS. You should either JMP(IGONE) if your custom code is okay or JMP (IERROR) if there is trouble. Try this:
newbasic:
CLC ;assume okay
INC $D020 ;or whatever you need to do...
BCS myerr ;oh no!
JMP (IGONE) ;okay, continue BASIC program
myerr:
LDX #1 ;error number (TOO MANY FILES, but you can change number for other message)
JMP (IERROR) ;stop program, print error
I tested this quickly in VICE and it works fine:
10 FORX=1TO8:&:NEXT
and
10 OPEN1,3
20 &
30 CLOSE1
40 &:?40
50 END
There might be some more subtle issues I didn't see...
Wow, thanks! You've taught me even more, now.
Yeah, I'm still working on the project, not much lately. I'm trying to get the inventory and GET and DROP routines working. It's kind of a pain.
I did put some work into the "Wall Bar and Grill" earlier this year, and you can do a few more things there now. Kinda fun. :)
Here's a bit of logic I came up with a while back to print singular/plural words:
10 n=1
20 print"played"n;
30 printleft$("games",4-(n<>1))
Looks a bit neater IMHO than:
20 print"played"n"game";:ifn<>1thenprint"s";
30 print
Yeah, there is a lot of stuff in that archive, isn't there... Working on consolidating stuff a bit. :)
I need to update the web page... I've got my Image BBS stuff there as well, and that could use an update as well.
http://cbbsoutpost.servebbs.com/dragonseye/projects
Although I agree the old
IF N<>1 THEN ?"S"
routine seems lame compared to your mathmatic approach, there are 3 reasons why the old approach is good.
1. Fewer bytes of code
2. Fewer constants (only 1 in "N<>1", but 2 in "4-(N<>1)")
3. No temporary string (but LEFT$ will create a temp. string)
Each of the points above means your BASIC program will operate a tiny bit faster. The last point can make a big difference... each time a temporary string is created, it reduces available RAM until, eventually, garbage collection is called. On a C128 this not so bad, but on a C64, garbage collection can take a very long time... like a minute or more! And STOP does not work in this time... many users think the computer has crashed when this happens... (STOP + RESTORE will work, but it may corrupt variable memory)
Good luck on your updates!
How about an even more obfuscated variant? ;D
20 PRINT"PLAYED";N;"GAME";CHR$(-83*(N<>1))
The above points are good ones, Hydrophilic. Maybe I will just stick with the "old" way of doing things.
Not to say my BASIC code is a paradigm of efficient coding, inasmuch BASIC can be.
That line of code is neat, Anders. I had to paste it into VICE (then lowercase it, a quirk I hadn't taken into consideration; my text editor does have a "convert selection to lowercase" function though). I assume it prints a chr$(0) if <>1? There certainly is no CHR$(-1)...
People continue to amaze me with coding feats. :)
Yup, CHR$(0). I'm not entirely sure what it will do to the computer display, probably nothing. The fact that an evaluated expression returns 0 or -1 is something I use as often as I can in my Basic programs. Perhaps not always it is most efficient way of solving things, but sometimes I can replace up to 4-5 IF statements with one single statement.
Take this rather typical Basic program:
10 x=0:y=0:s=1024
15 get a$:if a$="" then 15
17 poke s+y*40+x,32
20 if a$="w" and y>0 then y=y-1
25 if a$="z" and y<24 then y=y+1
30 if a$="a" and x>0 then x=x-1
35 if a$="s" and x<39 then x=x+1
40 poke s+y*40+x,81:goto 15
Easy to understand and follow, but a whopping four IF statements to move a ball around the screen. Now the rather obfuscated but shorter program:
10 x=0:y=0:s=1024
15 get a$:if a$="" then 15
17 poke s+y*40+x,32
20 x=x+(a$="a" and x>0)-(a$="s" and x<39)
25 y=y+(a$="w" and y>0)-(a$="z" and y<24)
40 poke s+y*40+x,81:goto 15
If required, those two assignments on lines 20-25 can even be put on the same line if space requires, or at least some other code can follow on the same line. Possibly you could even combine variables X and Y into a single one, but it would require some smart boundary checking. If the screen had been exactly 32 columns instead of 40, one could've used boolean AND to split a position index into columns and rows.
Hey, I saw that on the Denial forums. I saved it in my programming notes, 'cuz I'd never seen anything like that before. Image BBS uses a lot of Boolean logic in the BASIC portions. Things like:
on-(an$="Y")-2*(an$="N")-3*(an$="Q")goto xxx,yyy,zzz:goto aaa
Pretty neat IMHO. Way to go!
Hey, I did it. I wrote a little BASIC ML loader. By myself! No peeking at Google or comp.sys.cbm. Just "Mapping the C64."
Y'know, the type that has one line with
10 sys 2061
in it.
*= $0801
; assemble to disk with <-5
.word line_link; line link
.word 10 ; line #
.byte $9e ; sys token
.text "2061"
.byte 0 ; end of line
line_link .word 0
inc $d021
rts
;---------------------------------------
Programmed in Style's mighty Turbo Macro Pro assembler. \o/
Great! Now finish up TADA in assembly! ;)
Heh! Right away, sir. :)