The following is a listing of the macros used in this source listing. You will be able to tell when a macro was used by a plus (+) sign to the left of the hex code produced in column two by the assembler.
ASLA: MACRO %L ASL A ENDM RORA: MACRO %L ROR A ENDS LSRA: MACRO %L LSR A ENDM ROLA: MACRO %L ROL ENDM FDB: MACRO %L DW REV (%1) IF '=%2' <> '=' DW REV (%2) IF '=%3' <> '=' DW REV (%3) IF '=%4' <> '=' DW REV (%4) IF '=%5' <> '=' DW REV (%5) ENDIF ENDIF ENDIF ENDIF ENDM LOCAL: MACRO PROC ENDM BYTE: MACRO IF '%I' = '=' %L DB $80+(((%2-*)&$7F) XOR $40 ) ELSE IF '%1' = '@@' %L DW ( %2 ) ELSE %L DB %1 ENDIF ENDIF ENDM Syntax Table Macro ; THIS MACRO IS USED TO SIMULATE THE ACTION OF THE ORIGINAL ; ASSEMBLER IN HANDLING SPECIAL SYNTAX TABLE PSEUDO OPS AND ; OPERANDS ; ; THE 'SYN' MACRO EXAMINES UP TO 4 ARGUMENTS FOR CERTAIN SPECIAL ; CASE NAMES. ; ; IF THE NAME 'JS' IS FOUND, IT GENERATES A SPECIAL 'RELATIVE ; SYNTAX JSR' TO THE LABEL FOUND IN THE NEXT PARAMETER ; ; IF THE NAME 'AD' IS FOUND. IT GENERERATES A WORD ADDRESS OF ; THE LABEL FOUND IS THE NEST PARAMETER ; ; ANY OTHER NAME IS ASSUMED TO BE A SIMPLE BYTE VALUE SYN: MACRO :SYAR2 SET '=%2'<>'=' :SYAR3 SET '=%3'<>'=' :SYAR4 SET '=%4'<>'=' IF '%1' = 'JS' %L DB $80+(((%2-*)&$7F) XOR $40 ) :SYAR2 SET 0 ELSE IF '%1' = 'AD' %L DW (%2) :SYAR2 SET 0 ELSE %L DB %1 ENDIF ENDIF IF :SYAR2 IF '%2'= 'JS' DB $80+(((%3-*)&$7F) XOR $40 ) :SYAR3 SET 0 ELSE IF '%2' = 'AD' DW (%3) :SYAR3 SET 0 ELSE DB %2 ENDIF ENDIF ENDIF IF :SYAR3 IF '%3'= 'JS' DB $80+(((%4-*)&$7F) XOR $40 ) :SYAR4 SET 0 ELSE IF '%3' = 'AD' DW (%4) :SYAR4 SET 0 ELSE DB %3 ENDIF ENDIF ENDIF IF :SYAR4 IF '%4'= 'JS' DB $80+(((%5-*)&$7F) XOR $40 ) ELSE IF '%4' = 'AD' DW (%5) ELSE DB %4 ENDIF ENDIF ENDIF ENDM
Yes, it’s true. There are some bugs in Atari BASIC. Of course, that’s not surprising, since Atari released the product as ROM without giving the authors a chance to do second-round bug-fixing. But what hurts, a little, is that most of the fixes for the bugs have been known since June of 1979.
As this book is being written, rumor has it that at last Atari is in the final stages of releasing a new version of the BASIC ROMs. Unfortunately, these modified ROMs will appear too late for us to comment upon them in this edition. On the other hand, there are supposed to be fewer than twenty fixes implemented (which isn’t a bad record for a product as mature as Atari BASIC), so those of you who are willing to PEEK around a bit can use this listing as at least a road map to the new ROMs.
In any case, though, we thought it would be appropriate to mention a few of the bugs we know about, show you why they exist, and tell how we fixed them back there in the summer of ’79.
In the course of editing a BASIC program, sometimes the system loses all or part of the program, or it simply hangs. Often, even SYSTEM RESET will not return control to the user.
Also, string assignments that involve the movement of exact multiples of 256 bytes do not move the bytes properly. For example, A$=B$(257,512) would actually move bytes 513 through 768 of B$ into bytes 257 through 512 of A$, even if neither string were DIMensioned to those values.
Both of these are really the same bug. And both are caused because we strove to be a little too efficient.
There are many ways to move strings of bytes using the 6502’s instruction set. The simplest and most-used methods, though, are excruciatingly slow. So Paul and Kathleen invented a super-fast set of move-memory routines, one for moving up in memory (EXPAND, at $A881) and one for moving down in memory (CONTRACT, at $A8FD). Unfortunately, the routines are very complex (which is what makes them fast) and difficult to interface with properly. And so a bug crept into CONTRACT.
Take a look at the code of FMOVER ($A947). When we get here, we expect MVLNG to contain the complement of the least significant byte of the move length while MVLNG+1 contains its most significant byte. But look what happens if the original move length was, for example, $200. The complement of the least significant byte ($00) is still zero ($00), so the BEQ to :CONT4 occurs immediately.
But by then, the X register contains the number of pages to move plus one (X would contain 3 in this example), so we increment it (it becomes 2) and go to label :CONT3, where we bump the high-order byte of both the source and destination addresses. Ah, but therein lies the rub! We haven’t yet done anything with the first values in those source and destination addresses, so we have effectively skipped 256 bytes of each!
The solution is to replace the BEQ :CONT4 at $A94E with the following code:
DEX BNE :CONT2 RTS
Do you see the difference? If we enter with MVLNG equal to zero, we immediately move 256 bytes (at : CONT2) before ever attempting to change the source and destination addresses.
And this fix works, honest. We’ve been using it like this for over two years BASIC A+.
Taking the unary minus of a number (A=0 : PRINT -A) can result in garbage. Usually, this garbage will not affect subsequent calculations, but it does print strangely. And how did this come about?
We simply forgot to take into consideration the fact that zero doesn’t really have a sign. Look at the code for the unary minus operator (XPUMINUS, at $ACA8). Do you see the problem? We simply invert the most significant bit (the sign bit) of the floating point number in FR0.
What we should have coded would be something like this:
LDA FR0 BEQ :NOINVERT EOR #$80 STA FR0 :NOINVERT
Luckily, this is not too severe a problem to the BASIC user (one can always use “PRINT 0-A” instead of “PRINT A”) but just think — it only cost two bytes to fix this bug.
The GET statement does not reinitialize its buffer pointer, so it can do nasty things to memory if used directly after a statement which has changed the system buffer pointer. For example, GET can change the line number of a DATA statement if it is used after a READ. Also, the same problem exists for the LOCATE statement, since it calls GET.
From BASIC, the easiest solution is to use a function or statement which is known to reset the pointer. Coding “XX=STR$(0)” works just fine, as does PRINTing any number.
Within the source listing, the problem exists at location $BC82, label GET1. If the code had simply read as follows, there would be no bug:
GET1 JSR INTLBF ; reset buffer pointer LDA #ICGTC ; continue as before
Using either an INPUT or READ statement without a following variable does not cause a syntax error (as it should). Then, attempting to execute a statement such as 20 INPUT can cause total system lock-up.
The solution from BASIC? Be careful and don’t do it.
And this is one bug that we will not show the fix for, simply because it’s too long and involved. We will, however, point to labels :SINPUT and :SREAD (at locations $A6F4 and $A6F5) in the Syntax Tables and show why the bug exists.
Note that the :SINPUT does a syntax call (SYN JS,) to the :OPD syntax, which looks for — but does not insist upon — a file number specifier (#<numeric expression>). Then the syntax joins with :SREAD, which looks for zero or more variables.
Oops! Zero or more? Shouldn’t that be one or more? That’s where the problem lies.
In all too many cases, the use of the NOT operator is guaranteed to get you in trouble. If you don’t believe it, try this: PRINT NOT NOT 1.
The explanation of why the bug occurs is too lengthy to give in detail here; suffice it to say that the precedence of NOT is wrong. Remember the Operator Precedence Table we displayed in Chapter 8 of Part 2? Look at what you got for the go-onto-stack and come-off-stack precedence values for NOT.
Or look at location $AC57, the NOT entry in OPRTAB. NOT uses a 7 for both its precedence values. But wait a minute. If two operators have the same apparent precedence (as in NOT NOT A or even A + B + C), the expression executor will pop the first one off the stack and execute it. But with a unary operator, there is nothing to execute yet.
And the same bug exists for both unary minus and unary plus, so - -3 and + +5 don’t execute properly. Of course, since unary plus doesn’t really do anything, it doesn’t matter. In the case of unary minus, though, all but the last minus sign in a string of minus signs is ignored (that is, - -3 produces -3 as a result, instead of +3, as it should). But, by an incredible coincidence, the damage that unary minus causes is invisible to Execute Expression as a whole and only produces the error noted.
The fix? Well, if we want to leave NOT where it is in the order of things, the only way is to restructure the whole precedence table. But if we are willing to accord it a very high precedence, like unary plus and minus, we can fix it — and plus and minus — by changing the bytes at $AC57, $AC64, and $AC65 to $DC. And, thanks to the differing go-onto-stack and come-off-stack values, we can stack as many NOTs, pluses, or minuses as we want.
Are these all the bugs we know about that can be fixed easily? No. But these are the easiest to understand or the easiest to fix, and we thought they were instructive.
Of course, unless you have an EPROM board and burner handy, you may not be able to take advantage of these fixes. But at least now you may be able to work around them as you program with good old buggy-version Atari BASIC.
And take heart. Remember Richard’s Rule: Any nontrivial piece of software has bugs in it. And the corollary: Any piece of software which is bug-free is trivial.
AADD AF52 CGTO 0017 CVFPI AD56 EREXPR AAE0 AAPSTR AB98 CILET 0036 CVIFP D9AA EXOPOP AB0B n ADC AF53 CIO E456 DATAD 00B6 EXP DDC0 ADFLAG 00B1 CIX 00F2 DATALN 00B7 EXP1 DE03 n AFP D800 CLALL1 BD4F n DCBORG 0300 EXP10 DDCC AMUL1 AF5D CLE 001D DEGFLG 00FB EXP2 DE20 AMUL2 AF46 CLEN 0042 DEGON 0006 EXP3 DE26 APHM 000E CLIST 0004 DIGRT 00F1 EXPAND A881 ARGOPS 0080 CLPRR 002B DIRFLG 00A6 EXPERR DE4B ARGP2 AC06 CLSALL BD41 DNERR BCB0 EXPINT AB2E ARGPOP ABF2 CLSYS1 BCF1 DOSLOC 000A EXPLOW A87F ARGPUS ABBA CLSYSD BCF1 DSPFLG 02FE EXPOUT DE4A ARGSTK 0080 CLT 0020 ECSIZE 00A4 EXPSGN DE39 ARSLVL 00AA CMINUS 0026 EEXP 00ED EXSVOP 00AB ARSTKX 00AA CMUL 0024 ELADVC BADD EXSVPR 00AC n ASCIN D800 CNE 001E ENDSTA 008E FADD DA66 ASLA mac CNFNP 0044 ENDVVT 0088 n FASC D8E6 ATAN BE77 CNOT 0028 ENTDTD 00B4 FBODY 000C ATAN1 BE9A COLD1 A008 EPCHAR 005D FCHRFL 00F0 ATAN2 BED4 COLDST A000 ERBRTN B920 FDB mac ATCOEF DFAE COLOR 00C8 ERGFDE B922 FDIV DB28 ATEMP 00AF COMCNT 00B0 ERLTL B924 FHALF DF6C ATNOIT BEE2 CON 001E ERNOFO B926 FIXRST B825 BININT 00D4 COSTLO A8FB ERNOLN B928 FLD01 DD8F BOTH BDB3 CONTRA A8FD n ERON B93E n FLD0P DD8D BRKBYT 0011 COPEN BBB6 EROVFL B92A FLD0R DD89 BYELOC E471 COR 0029 ERRAOS B92C FLD11 DD9E n BYTE mac COS BDB1 ERRDIM B92E FLD1P DD9C C 0044 COX 0094 ERRDNO B918 FLD1R DD98 BYELOC E471 CPC 009D ERRINP B930 FLIM 0000 n BYTE mac CPLUS 0025 ERRLN B932 FLIST BAD5 C 0044 CPND 001C ERRNSF B916 n FLOG10 DF01 CAASN 002D CR 009B ERRNUM 00B9 FLPTR 00FC CACOM 003C CREAD 0022 ERROOD B934 FMOVE DDB6 CADR 0043 CREGS 02C4 ERROR B940 PMOVE1 DDB8 CALPRN 0038 CRPRN 002C ERRPTL B91A FMOVER A947 CAND 002A CRTGI BFF9 ERRRAV 003C FMPREC 0005 CASC 0040 CSASN 002E ERRSSL B936 FMUL DADB CCHX 0031 CSC 0015 ERRVSF B938 n FNTAB A829 CCOM 0012 CSEQ 0034 ERSVAL B91C FONE DE8F CCR 0016 CSGE 0031 ERVAL B93A FP9S DFEA CDATA 0001 CSGT 0033 ESIGN 00EF FPI D9D2 CDIV 0027 CSLE 002F EVAADR 0002 FPONE BE71 CDLPRN 0039 CSLPRN 0037 EVAD1 0004 FPORG D800 n CDOL 0013 CSLT 0032 EVAD2 0006 FPREC 0006 n CDQ 0010 CSNE 0030 n EVARRA 0040 FPSCR 05E6 CDSLPR 003B CSOE 0011 EVDIM 0001 FPSCR1 05EC CEOS 0014 CSROP 001D n EVNUM 0001 FPTR2 00FE CEQ 0022 CSTEP 001A EVSADR 0002 FR0 00D4 CERR 0037 CSTR 003D EVSCAL 0000 FR0M 00D5 CEXP 0023 CTHEN 001B EVSDIM 0006 FR1 00E0 CFFUN 003D CTO 0019 EVSDTA 0002 FR1M 00E1 CFLPRN 003A CUMINU 0036 EVSLEN 0004 FR2 00E6 CFOR 0008 CUPLUS 0035 EVSTR 0080 FRA10 DD01 CGE 001F CUSR 003F n EVTYPE 0000 FRA1E DD09 CGOSUB 000C CVAFP D800 n EVVALU 0002 FRA20 DD05 CGS 0018 CVAL 0041 EXECNL A95F FRA2E DD0F CGT 0021 CVFASC D8E6 EXECNS A962 FRADD AD3B FRCMP AD35 n ICOIN 0001 LSRA mac RNDDIV B0A8 FRCMPP AD32 ICOIO 0003 LSTMC B63D RNDLOC D20A FRDIV AD4D n ICOOUT 0002 MAXCIX 009F ROLA mac FRE 00DA n ICPBC 000A MDEND DB1A ROM A000 FRMUL AD47 n ICPBR 0008 MDESUP DCCF RORA mac FRSIGN 00EE ICPTC 000B MDSUP DCE0 RSHF0E DC62 FRSUB AD41 n ICPTR 0009 MEMFUL B93C RSHFT0 DC3A FRUN BAF7 ICPUT 0346 MEMTOP 0090 RSHFT1 DC3E FRX 00EC ICSBRK 0080 n MEND1 DB1E RSTPTR B8AF FSCR 05E6 n ICSDER 0083 MEND2 DB21 RSTSEO BD99 FSCR1 05EC n ICSDNR 0081 MEND3 DB24 RTNVAR AC16 FSQR BF08 n ICSEOF 0003 MEOLFL 0092 RUNINI B8F8 FST01 DDAD n ICSIVC 0084 MISCR1 0480 RUNSTK 008E n FST0P DDAB n ICSIVN 0086 MISCRA 0500 SAVCUR 00BE FST0R DDA7 n ICSNED 0082 MV0TO1 DDB6 SAVDEX 00B3 FSTEP 0006 n ICSNOP 0085 MVFA 0099 SCANT 00AF FSUB DA60 n ICSOK 0001 MVFR0E DD34 SCOEF BE41 FTWO BF93 ICSTA 0343 MVFR12 DD28 SCRX 0055 GDIO1 BC22 ICSTAT 000D MVLNG 00A2 SCRY 0054 GDVCIO BC1D n ICSTR 0002 MVTA 009B SCVECT BFF9 GET1 BC82 n ICSWPE 0087 NATCF 000B SEARCH A462 GET1IN ABE9 IFP D9AA NCTOFR AB4D SETDZ BD72 GETINT ABE0 n ILSHFT DA5A NIBSH0 DBEB SETLIN B818 GETLL A9DD INBUFF 00F3 NIBSH2 DBE7 SETLN1 B81B n GETPI0 ABD8 INDEX2 0097 NLCOEF 000A SETSEO BD79 GETPIN ABD5 INTLBF DA51 NOCD0 BD01 SGNFLG 00F0 GETSTM A9A2 IO1 BD0A NORM DC00 SICKIO BCB9 GETTOK AB3E n 102 BD0E NORM1 DC04 SIN BDA7 GETVAR AB89 IO3 BD10 NPCOEF 000A SINDON BE40 GFDISP 0003 IO4 BD12 NSCF 0006 SINERR BDA5 GFHEAD 0004 IO5 BD19 NSIGN 00EE SINF1 BDF6 n GFLNO 0001 n IO6 BD1D NXTSTD 00A7 SINF3 BE00 GFTYPE 0000 IO7 BD24 ONLOOP 00B3 SINF4 BE11 GIOCMD BD04 IO8 BD26 OPETAB AA70 SINF5 BDE4 GIODVC BC9F IOCB 0340 OPNTAB A7E3 n SINF6 BDD5 GIOPRM BD02 IOCBOR 0340 OPRTAB AC3F SINF7 BDCC GLGO BA92 IOCMD 00C0 OPSTKX 00A9 n SINOVF BDCB GLINE BA89 IODVC 00C1 OUTBUF 0080 SIX 0480 GLPCX BAC4 IOTES2 BCB6 P10COF DE4D SKBLAN DBA1 GLPX BAC6 IOTEST BCB3 n PATCH BDA4 SKCTL D20F n GNLINE BA80 ISVAR BD2F PATSIZ 0001 SKPBLA DBA1 GNXTL A9D0 ISVAR1 BD2D PIOV18 BE6B SNTAB A4AF GRFBAS 0270 n LBPR1 057E PIOV2 BE5F SNX1 A050 GSTRAD AB9B n LBPR2 057F PIOV4 DFF0 SNX2 A053 GTINTO ABE3 LBUFF 0580 PLYARG 05E0 SNX3 A05D GWTAD AC28 LDDVX BCA6 PLYCNT 00EF SOPEN BBD1 HIMEM 02E5 LDIOST BCFB PLYEV1 DD5B SOX 0481 HMADR 02E5 LDLINE B578 PLYEV2 DD6F SPC 0482 n IBUFFX 00A9 LELNUM 00AD PLYEVL DD40 SQR BEE5 ICAUX1 034A LGCOEF DF72 PLYOUT DD88 SQR1 BF00 ICAUX2 034B LISTDT 00B5 POKADR 0095 SQR10 DF66 ICAUX3 034C LLINE B55C POP1 AC0F SQR2 BF84 ICAUX4 034D LLNGTH 009F POPRST B841 SQR3 BF8A ICAUX5 034E LMADR 02E7 PRCHAR BA9F SQRCNT 00EF ICBAH 0345 LOADFL 00CA PRCR BD6E SQRDON BF64 ICBAL 0344 LOCAL mac PRCX BAA1 SQRERR BEE3 ICBLH 0349 LOG DECD PRDY1 BD59 SQRLP BF2A ICBLL 0348 n LOG1 DEDB PREADY BD57 SQROUT BF92 ICCLOS 000C LOG10 DED1 PROMPT 00C2 SRCADR 0095 ICCOM 0342 LOG10E DE89 PSHRST B683 SRCNXT A490 n ICDDC 000E LOG2 DEEF PSTR B480 SRCSKP 00AA n ICDNO 0341 LOG3 DEF3 PTABW 00C9 SREG1 D208 ICDRAW 0011 LOG4 DEF9 PUTCHA BA9F SREG2 D200 n ICFREE 00FF LOG5 DEDB QTEMP 00D9 SREG3 D201 n ICGBC 0006 LOG6 DF46 RADFLG 00FB SSTR BA73 n ICGBR 0004 LOG7 DF53 RADON 0000 STACK 0480 ICGR 001C LOGBTH DED3 RESCUR B6BE STARP 008C ICGTC 0007 LOGDON DF64 RISASN AEA6 STENUM 00AF ICGTR 0005 n LOGERR DED9 RISC AB64 STETAB AA00 n ICHID 0340 n LOGOUT DF56 RML 0007 STINDE 00A8 ICLEN 0010 LOMEM 0080 RMSG BD67 STKLVL 00A9 n ICMAX 000E LPRTOP B535 STMCUR 008A STMLBD 00A7 XDATA A9E7 XPCHR B067 XPSNE ACBE STMSTR 00A8 XDEG B261 XPCOS B125 XPSQR B157 STMTAB 0088 XDIM B1D9 XPDIV AC9F XPSTIC B026 STOP B7A7 XDOS A9EE XPDLPR AD82 XPSTR B049 STOPLN 00BA XDPSLP AD82 XPEQ ACDC XPSTRI B02E STRCMP AF81 XDRAWT BA31 XPEXP B14D XPUMIN ACA8 SVCOLO 02FB XEND B78D XPFLPR AD7B XPUPLU ACB4 SVDISP 00B2 XENTER BACB XPFRE AFEB XPUSH AD16 SVESA 0097 XERR B91E XPGE ACD5 XPUSR B0BA SVONTC 00B0 XFALSE AD00 XPGT ACCC XPUT BC72 SVONTL 00B2 XFMFLG 00F1 XPIFP AFD1 XPVAL B000 SVONTX 00B3 XFOR B64B XPIFP1 AFD5 XRAD B266 SWNTP 00AD XFORM DE95 XPIFP2 AFD8 XREAD B283 SWVTE 00B1 XGET BC7F XPINT B0DD XREM A9E7 SYN mac XGO1 B6AE XPL10 B143 XREST B26B SYNTAX A060 XGO2 B6A6 XPLE ACB5 XRTN B719 TEMPA 00C4 XGOSUB B6A0 XPLEN AFCA XRUN B74D TENDST A9E2 XGOTO B6A3 XPLOG B139 XSAASN AEA3 n TESTRT A9E7 XGR BA50 XPLOT BA76 XSAVE BB5D TOPRST 0090 XGS B6C7 XPLPRN AB1F XSAVE1 BB62 TRAPLN 00BC XGS1 B6CA XPLT ACC5 XSETCO B9B7 TSCOX 00AB XIF B778 XPMINU AC8D XSOUND B9DD TSLNUM 00A0 XINPUT B316 XPMUL AC96 XSTATU BC28 TSTALP A3F7 XINT B0E6 XPNE ACBE XSTOP B793 TSTBRK A9F4 XITBT B354 XPNOT ACF9 XTF AD09 TSTEND B910 XLET AAE0 XPOINT BC4D XTI AD07 TSTNUM DBAF XLIST B483 XPOKE B24C XTRAP B7E1 TVNUM 00D3 XLOAD BAFB XPOP B841 XTRUE AD05 TVSCIX 00AC XLOAD1 BB04 XPOR ACEE XXIO BBE5 TVTYPE 00D2 XLOCAT BC95 XPOS BA16 ZF1 DA46 VNTD 0084 XLPRIN B464 XPPDL B022 ZFP 00D2 VNTP 0082 XNEW A00C XPPEEK AFE1 ZFR0 DA44 VNUM 00D3 XNEXT B6CF XPPLUS AC84 ZICB 0020 VTYPE 00D2 XNOTE BC36 XPPOWE B165 ZPADEC AFBC WTP 0086 XON B7ED XPPTRI B02A ZPG1 0080 WARMFL 0008 XOP1 BBED XPRINT B3B6 ZTEMP1 00F5 WARMST A04D XOP2 BBFB XPRND B08B ZTEMP2 00C6 WWTPT 009D XOPEN BBEB XPRPRN AD7B ZTEMP3 00F9 XBYE A9E8 XPAASN AD5F n XPSEQ ACDC ZTEMP4 00F7 XCLOAD BBAC XPABS B0AE XPSGE ACD5 ZVAR B8C0 XCLOSE BC1B XPACOM AD79 XPSGN AD19 ZXLY DA48 XCLR B766 XPADR B01C XPSGT ACCC XCMP AD26 XPALPR AD86 XPSIN B11B XCOLOR BA29 XPAND ACE3 XPSLE ACB5 XCOM B1D9 XPASC B012 XPSLPR AE26 XCONT B7BE XPATN B12F XPSLT ACC5 XCSAVE BBA4