Fred and Lou, After Anton presented this rather extensive document to me I decided it would be rather silly for me to send you my meager comments. Essentially mine were a less extensive version of his, with a number of graphic commands added. I agree totally with all of these suggestions with the possible exception of some of his "commentary" on the state of existing system. Although both of us realize that given the time considerations involved in this project many of these things might have to go by the wayside we do think they are worth considering...and implementing! --Loren Notes on System ROM Software for Proposed New Commodore Machine by Anton Treuenfels Last revision: 21 May 88 The following ideas are more in the nature of suggestions rather than a blueprint, as time does not permit a thorough critique, redesign, and reimplementation of the existing system software (desirable as that might be, particularly in the case of the BASIC interpreter). As such, the ideas presented here will vary in originality, quality, desirability, ease of implementation, consistency with previous versions of the system software, consistency with each other, and so on and so forth. Caveat emptor. Category: Internal BASIC program text format 1) Tokenization a) Backward compatibility with existing Commodore BASIC programs is highly desirable. It is impossible to achieve complete compatibility with all previous versions of Commodore BASIC strictly by provisions in the interpreter for handling the tokens produced by those versions, if only because those versions themselves produced incompatible tokenizations (BASIC 2.0 and BASIC 3.5, for example). What might be considered instead is a "pre-processing" arrangement. This would be a program, probably written using the native version of BASIC, which simply reads in a BASIC program text file and replaces the tokens found there with the tokens used by the native version. Several other useful operations could be easily accomplished by a such a pre-processor. Tokenization could be regularized and cleaned up - no more mixing and matching of single- and double-token keywords, no more need to provide run-time support for archaic constructs such as "GO TO" as two separate keywords. Incompatibilities between the old and current versions of BASIC could be flagged at this time. b) Complete the move to double-tokenization, so that all reserved words are changed to pairs of tokens. Let the first token flag the class of the keyword, similar to the way BASIC 7.0 uses the first token ($FE for statements and $CE for functions), but more extensive. Other classes and subclasses might include: pseudo-variables; i/o statements; editor commands; immediate- or program-mode only, both, non-executable; program flow control statements; string functions, real functions; modifiers (WHILE, UNTIL) and subordinate keywords (TO, STEP, USING) which can't appear at the start of a statement. One scheme might be to use the high nybble to flag class (statement, function, pseudo-variable) and the low nybble to flag subclass. Assigning keyword tokens in this manner is not strictly necessary to the proper functioning of a BASIC interpreter, but it does allow some error checking to be performed simply by knowing whether or not some actions are permitted for a certain class of keyword. For example, keywords which can only be used in immediate- or program-mode can have that check performed by the main dispatch loop rather than doing so individually. Category: BASIC language editor 1) Modifications to BASIC 7.0 keywords a) RENUMBER should permit the renumbering of line ranges rather than insisting on renumbering the entire program beyond the specified starting line number. The primary problem anticipated here would be the possibility that the last line in the range renumbered would be given a line number higher than the first line beyond the range renumbered. This is an easy situtation to detect before any permanent changes to the program text have been made, and should cause an error. Also, the present version of the RENUMBER routine scans through the program text twice. The first time is a kind of 'dummy' renumbering which apparently has as one purpose detecting whether or not the sum of the changes to the program text would cause line length or text space overflows. If there are RAM banks not used to store program text (as on the C128), perhaps a 'backup' copy of the program text could be made before alterations of the text are made. Then, if a problem is encountered during renumbering, the 'backup' copy could be copied back to the main text space. This might improve overall performance, and could also be used with any editing command that alters text (ie., CHANGE, ENTER, MERGE, etc.). (on the C128 this practice would have the side effect of destroying the contents of variable memory. The relative stability of variables on the C128 is actually quite handy, and it would be shame to lose that). 2) New Keywords a) FIND by line range and, perhaps, tokenized/untokenized status. Also, some degree of regular-expression pattern-matching ala Kernighan & Plauger's "Software Tools" should be permitted (eg., wildcard match, match any member of class, match absence rather than presence, etc.). Although perhaps not commonly used during BASIC program text editing, a properly written matching subroutine of this type could also be used by the INSTR() function, where it would be extremely handy. b) CHANGE, same capabilities as FIND. c) MERGE and/or ENTER - variants of the same idea, these would input program text from a file rather than from the keyboard. The difference between them would be that MERGE handled tokenized text while ENTER dealt with untokenized PETASCII text (leaving only ASCII and screen-coded text to worry about). The simplest (and slowest) way to implement file entry of untokenized text would be to have a method of setting the default input device to something other than the keyboard (similar to what the CMD# statement does for the default output device). The BASIC direct mode input loop calls the Kernel CHRIN routine, which would after redirection take its input from a file. BASIC would behave normally, fetching characters and placing them in the input buffer until a carriage return is spotted, tokenizing the line, and returning for another line. The main difficulty appears to be gracefully recognizing when to stop and return input control to the keyboard. This ought to occur automatically after reaching the end-of-file or in case of error (the error routine would set the default input to keyboard the same way it presently sets default output to screen). Assuming such an input redirection capability, it might be nice to implement it as a usable keyword as well. c) DLIST or FLIST lists a program file on disk (or tape) to the current output device. Specifiable by line range as with the existing LIST command. d) TYPE reads a sequential file from disk and sends it to the current output device. Automatic replacement of non-printable characters with a space or period, similar to what machine-language monitors normally do. Ability to handle ASCII as well as PETASCII files, perhaps with some syntax such as TYPEA "FILENAME" 3) Scrolling On-screen forward and reverse scrolling through the BASIC program text is very useful. It is a bit difficult to decide just where this capability should be implemented, however - isolation of function suggests that it would not be a terribly good idea to let the screen editor know too much about the internal structure of BASIC program texts (eg., that would make it difficult to persuade the screen editor to let the machine-language monitor also scroll the screen for its own purposes). The existing screen editor indirect vectors are sufficient for implementing this behavior if a convention was adopted that BASIC would take over these vectors at entry (warm and cold starts) and restore them upon exit (MONITOR certainly, perhaps also SYS, USR(), Kernal calls, others? Still, control is supposed to return to BASIC except in the case of MONITOR, so perhaps that is the only time it would be necessary to replace the original vectors). Another possiblity would be to implement a new screen editor indirect vector which would be taken whenever an appropriate cursor movement at the edge of the screen occured. This would still have to be initialized and given up at appropriate times by independent users. Category: BASIC Variables 1) Variable Names Long variable names (ie., more than two meaningful characters, say maybe eight instead) would be nice, although they would slow down performance of the interpreter if used (increased text scanning time, if nothing else - the search of the variable table might be increased in speed enough to compensate, however, see below). 2) Variable Storage a) A fair amount of interpretive time is spent searching the variable table, and performance might be improved by alternative arrangements. The variable table might be rearranged as a series of lists linked by type (ie., all reals linked together, all integers linked, all strings, all function pointers). This would speed up search time since only the correct type would ever be scanned during search. The lists might even include array variables, thus turning two tables into four lists. Since type distinguishers within linked lists of variables would be unnecessary, they also enable the addition of new data types relatively painlessly (see below). Alternatively, the (long) name of the variable might be hashed to point to the start of one of a number of linked variable lists (say, eight to 32 or so). b) Each variable type might be given its own RAM bank (provided RAMtexpansion is in place), so that program text is in one bank, real variables in another, strings in another, and so on (offhand there would seem to be a number of advantages in giving strings their own RAM bank, over and above the possible benefits of making it easier for the interpreter to locate them). c) RAM space might be dedicated for, say, all one-character non-array variables, so that they could be found extremely quickly (the name itself serving as an index into a table of offsets from the start of dedicated storage). Memory space requirements for this would be minimal, however it might complicate such operations as clearing variables and garbage collection. One method of implementing this might be to have the first reference to any variable allocate space at the start of the variable table for all of them (at this point no harder than allocating space for a single variable) and, if it is necessary that other interpreter or user-written routines be able to find them by name or type within a single variable table, name them. With the four current variable types, over fifty very common and over fifty less common variables could be accessed at the same rate ("fast" variables). "Slow" (two-character) variables could also be accessed a bit more quickly than under the current scheme, since "fast" variables would automatically be skipped. This idea raises some complications if used in connection with the linked-lists idea discussed earlier, not insurmountable but perhaps requiring too much finicky attention to detail to do both in a short time. It would appear to work very well as-is with the separate RAM bank for each type idea. Faster searching for a known class of variables might encourage over-reliance on that class by programmers, resulting in decreased readability and maintainability of programs. 3) Data Types a) A 'bitmap' type in which information is bound to single bits instead of groups of bits. Binary operations allowed would include the sixteen logical operations that can be done when combining two bitmaps during a graphics bitblit (which include everything that can be done with Pascal's 'set' and 'Boolean' data types). Unary operations would include things such as add member, delete member, test if member, perhaps count members, and so on. One way to organize such a type internally would be to model them after the way strings are currently handled, as a descriptor containing a length and pointer to the bitmap itself. b) User-defined compound (structured) types made up of collections of primitive types. Implementation difficulty probably high. c) High-level support for aggregate types other than arrays, such as stacks, queues, linked lists, and trees. Admittedly most of these can be adequately modelled with arrays (although the 'bitmap' type discussed above, which is a kind of aggregate Boolean type, could not), but the problems involved in doing so usually stem from not getting the picky management details right. The fact that arrays are adequate models suggests that all that need be done is implement a few additional management routines around the existing array code in the interpreter. For example, there might be pseudo-variables called "STACK", "EMPTY", and "FULL". The statement "A$(STACK)=EMPTY" initializes a stack associated with the array A$(). The statement "A$(STACK)=X$" pushes the contents of X$ onto the stack and "X$=A$(STACK)" pops the top element of A$ into X$. Statements like "IF A$(STACK)=FULL THEN EXIT" allow the user program to monitor the stack so that the error message "STACK FULL" doesn't appear. The underlying implementation mechanism might include space within the array header in the variable table for the current stack value. A two-byte unsigned integer should be sufficient. A heap could also be done with a single integer. A queue would require two integers. A common feature to these structures is that the allowed operations - init, add element, remove element - are easy to define and implement. Structures as linked lists and trees would require more integers to define and offer the prospect of random removal of elements, a messy business (additional power often tends to be like that). d) High-level support for graphics and sound objects other than as whatever happens fit into a string. This might be done simply by allowing collections of binary data to be BLOADed or POKEd into memory and then having keywords that act on those collections. This concept is somewhat similar to the way hardware sprites are handled by BASIC 7.0. It would be nice if the binary data could have some additional internal structure that might affect what the keywords did with them, eg., the 64th byte of a sprite definition could be used to hold color, size, priority, multicolor, and visibility data (4+1+1+1+1 bits) (this would have the drawback of making it more difficult to use the same bitmap image for more than one sprite, although BASIC 7.0's assignment of images to dedicated slots renders the possibility meaningless anyway). It would also be nice if such structures could be named, loaded, saved, and placed anywhere in memory (preferrably automatically, just like any other variable). Category: BASIC User-Defined Functions 1) Function Descriptors If program text moves around (as BASIC 7.0 graphic capabilities make it do), function pointers in the variable table should be kept as offsets from the start of program text rather than absolute addresses to eliminate the annoying and unnecessary practice of requiring statements to be executed in a particular order (DIM and DEF require certain specific ordering, but the emphasis here is on 'unnecessary'). 2) Function Capabilities a) Functions of more than one real variable, ie., FNA(X,Y). On the surface, this would seem to simply require pushing function variables on the stack until the closing parenthesis is reached, similar to the way array references work. b) Functions capable of returning different types, specifically string types, ie., FNA$(X$) or FNB$(Y$,X) (mixed multiple variables). On the surface this would not appear too difficult, especially since nested function calls are already possible, eg., "A$=MID$(B$,INSTR(B$,C$),6)". The primary difficulty in the case of strings would appear to be what to do about garbage collections occuring during function evaluation. Perhaps the temporary string stack could be expanded and string variables placed there during evaluation, so that the garbage collector could find them automatically. Alternatively, push the strings onto the same stack the reals go on and rewrite the garbage collector to know how to look there. c) Multiple line functions. Presumably, once variables go on the stack they stay there until a (new) keyword signalling the end of the function is executed. Category: BASIC Errors/Debugging 1) Error Messages More specificity might be considered. 'SYNTAX ERROR' in particular is annoyingly broad and to some extent needlessly so, as the condition which triggered the message can often be easily further identified (eg., 'MISSING DELIMITER' when a parenthesis or comma is not found, 'MISSING KEYWORD' if a statement does not start with a keyword or variable name, 'MISSING TEXT' if a statement ends too soon, 'MISSING TERMINATOR' if a statement ends too late, and so on). Error messages might be coded in a phrase table if text size becomes a concern. 2) Error Checking Certain categories of errors might be checked for at entry time, notably SYNTAX and TYPE MISMATCH errors. This would essentially require a trial parser, and perhaps also an indirect vector so the parser could be extended along with the rest of the interpreter. The scheme might work by identifying the keyword token(s) at the start of a statement and then applying a 'template-driven' parser subroutine. Templates would be tokenized but convey much the same information as keyword usage is defined in the manuals, eg., END EOS; FOR = TO [STEP ] EOS; PRINT [[][,][;][TAB()][SPC()]...] EOS. The subroutine would be passed a pointer to the desired template (user-supplied keywords would then supply pointers to their own templates) and either return peacefully or flag an error (an alternative to a pointer would be to put the template in a known table position similar to what is assumed when listing keywords, so that given a token the parser counts off to the proper position in the table. It might be possible to avoid a separate vector in this case - the start of the keyword table might be a pointer to the template table, for instance). The run time benefits of not having to repeatedly perform these (essentially static) checks seem obvious. There is also the benefit of not waiting (possibly for a very long time) until execution, so that the line's purpose is still fresh in the mind of the programmer. It would not be difficult to implement such a trial parser as an element of the main entry loop, nor would it increase tokenization/insertion time unacceptably. It might be somewhat more difficult to extricate all the scattered syntax checks from the main body of the interpreter (this would not actually be necessary for proper execution but not doing so would remove any speed benefit and also leave open the possibility of hostile user reaction). Also, if program text input from a file is supported, it might be useful to be able to turn off static error checking (so at least the stuff can be entered without continually breaking off). In this case checking would be deferred until turned back on (automatically at run time if not sooner by the user, so that all text is checked before reaching the main body of the interpreter - this amounts to a pre-run pass). Deferred checking would imply the loss of any keyword table information discovered during tokenizing, and would probably mean that a separate vector would definitely be required for extension keyword checking. 3) Tracing a) Trace output might be diverted to a disk or printer file independently of program output so the screen can be undisturbed. Something along the lines of "OPEN 4,4: TRON #4". Alternatively, if multiple screens are supported, trace output to a selected screen. Or perhaps only to one portion of a split screen. b) Trace only a given range of lines, so that a GOSUB call from within the range to a line outside the range suspends trace output for the duration of the call (this would seem relatively easy to implement). If no range specified, trace the entire program. 4) Slow Statement Execution Single-step statement execution specificable by line range. Pause until keypress (resume single step or resume full speed). A visual indication of single-stepping would be helpful. One way would be to allow single-stepping only when also tracing, although this might be overly restrictive. Another visual identifier might be to list the statement about to be executed. Also auto-stepping similar to single-stepping, except that execution resumes automatically after a programmable delay period. Category: BASIC String Functions 1) Modifications to BASIC 7.0 Keywords a) The INSTR() function is one of the most useful string functions available in BASIC 7.0, simply because pattern-matching is such a powerful concept. However in its present form INSTR() is limited to only exact matches. The pattern matching capability of INSTR() should be extended to recognize at least some of the "regular expression" syntax as described in Kernighan and Plauger's "Software Tools" (although the notion is not original with them). This would be a "metacharacter" approach similar to but more comprehensive than the present filename-matching capabilities of Commodore DOS. For example, "?" would match any character in the same position ("wildcard"), "[" and "]" would match any character between them ("character classes"), "-" would mean all characters between the two surrounding it (so "[a-z]" means "match any lower case character from 'a' to 'z' in this position"), "!" would negate the match of the following character ("! " means "match any nonspace character"), and "@" cancels any "metacharacter" meaning of the following character ("@!" matches "!"). The "closure" property signalled by "*" ("match zero or more occurences of the preceeding pattern") is useful but offhand seems to cause quite a few implementation headaches, although if the 1581 DOS version of the "*" match is well-written it might be adopted directly. b) HEX$() could be extended to accept a string argument. The string would be treated as a collection of 8-bit binary numbers and each would be expanded into two hexadecimal characters (high and low nybbles of the orginal binary character). The resultant string would be twice the length of the binary string argument. The complementary function BIN$() would convert in the opposite direction (see below). These type-conversion capabilities would be of most use if other keywords, such as graphics or sound keywords, required binary string arguments. The 'bitmap' variable type mentioned above would also become handier with this conversion readily available. 2) New Keywords a) "MAK$(, )" returns a string consisting of the first character of repeated times. This is a generalized version of the SPC$() function of some BASICs. If is null or is zero, return the null string. b) "SUB$(, [,])" works exactly like the MID$() function (on either side of the equals sign) unless the argument is present. In this case refers to the position of the last character affected rather than to number of characters affected. It is of course trivial to do this with MID$() if the start position and length are known beforehand. It is when these values must be determined dynamically that it becomes messy, eg., "A$=MID$(B$,INSTR(B$,C$),INSTR(B$,D$)-INSTR(B$,C$)". The effort saved here by a SUB$() function is slight but real. c) "BEG$(, )" and "END$(, )", like SUB$(), save a bit of effort over their existing counterparts "LEFT$(A$,INSTR(A$,B$)-1)" and "RIGHT$(A$,LEN(A$)+1-INSTR(A$,B$))". They can also provide null results instead of error messages in the event does not contain . d) "BIN$()" combines pairs of hex characters in into a single binary character and returns a binary string half the length of . The HEX$() function could be extended to provide the opposite conversion capability. These functions would be of most use in preparing strings for use by other keywords requiring binary string arguments, such as graphics or sound keywords. BIN$() would be particularly useful for converting data in DATA statements or on disk into useable form. e) "INPUT$( [,#lf])" obtains characters from a logical file (defaulting to the keyboard if no file# is explicitly declared) and assigns them to a string. The only terminating conditions are that is reached or the file status indicates no more characters are available (end-of-file or error). In particular this function speeds up reading data from disk files which may not be organized in such a way as to work effectively with the INPUT# statement. In general this function would not work well with RS232 files, although it could be used effectively with some kinds of file transfers in which data is transmitted in packets of known size. A variant form of this function would allow a string argument in place of . This would function as a pattern, so that input would continue until the pattern was matched (INPUT# might be considered exactly this sort of function with a fixed pattern). If such a pattern were allowed to be of the "regular expression" variety advocated above for INSTR() this function would become quite flexible indeed. f) XLAT$(,,) looks for occurances of characters from in and changes them to characters from . This kind of filtering action could be used to, say, translate (xlat) lower case to upper case and vice-versa, ASCII to PETASCII and vice-versa, unprintable characters to spaces, and so on. The translation changes a matched character in to the character in the corresponding position in . For example, if the third character in is found in , it is changed into the third character in . If there is no corresponding character in (because it is shorter than ), delete the character from . Category: BASIC Reserved (Pseudo-) Variables 1) Tokenization a) The BASIC 7.0 approach to tokenization involves a routine which is passed a pointer to a keyword table, which is then used during a scan of the input buffer. It would not be terribly difficult to add a table of reserved variable names to the tokenizer. This would permit longer names with no run-time penalty and release the currently-used shorter names back to the user. Reserved variables would become easily recognized by the to the variable look-up routine. The implementation of this idea might be a double-token scheme in which the first token flags "reserved variable". Certainly this wouldn't take up any more program text space than the current two-letter scheme. 2) Additional Reserved Variables a) Commonly used terms might be made into pseudo-variables, to both speed up and simply their use. The current tokenization of the mathematical quantity 'pi' by Commodore BASICs is an example of this. Additional candidates include other mathematical quantities ('e') and strings (chr$(0), chr$(13)). If the regular-expression syntax advocated is adopted, some strings will occur fairly often, eg., "[a-zA-Z0-9]". Category: BASIC Variable Manipulation 1) Modifications to BASIC 7.0 Keywords a) The SWAP keyword might be redefined altogether so as to exchange pairs of variables instead of manipulating RAM expansion memory. "SWAP A$,B$" would exchange the descriptors (and back pointers, if implemented) associated with A$ and B$ directly, bypassing the zero-page "accumulator" used by the expression evaluation routines. Error if the two variables are not of the same type. This saves quite a bit of time as opposed to using a third "temporary holding" variable and three assignments to accomplish the same thing. Instead of redefing SWAP a new keyword called EXCHANGE could be used. Still again, an "exchange" operator with the same precedence as the assignment operator could be introduced, perhaps designated by "==" or "<=>" or "#" or somesuch. This would be allowed in a LET statement in place of the normal assigment operator, eg., "A$==B$". 2) New Keywords a) An array sort. It would be almost impossible to create a sort routine that would meet every need, so the emphasis here should be on covering the most common requirements. At the least a system routine should sort singly-dimensioned arrays of any variable type in either ascending or descending order with a simple syntax. For example, "SORT A$(30),A$(90)" which would sort elements 31 through 91 of array A$() in ascending order. "SORT A$(90),A$(30)" would sort the same elements in descending order. The syntax also makes it easy to guarantee that the sort routine would be working with an array large enough to avoid problems of sorting things that were not actually part of the array. Desirable additional features mostly center around the ability to compare one set of objects but have some other set(s) of objects be rearranged as well as or instead of the set being compared. Multiply-dimensioned arrays, cosorts, and keysorts all fall in this category. In the BASIC environment possibly the most useful of these is cosorting. The syntax above could be extended so that "SORT X$(A),X$(B),Y(C),Y(D)" exchanges elements of the array Y() whenever elements of X$() are exchanged. If X$(A)=X$(B), then compare Y(C) and Y(D). This kind of sort is relatively simple to implement as a single sort management routine using indirect links to separate find, compare, and exchange routines for each variable type. As far as algorithms go, a Shell sort is relatively short, requires little variable space, and performs well with sorted or unsorted data. Category: BASIC IRQ Routines 1) Modifications to Existing Routines a) The existing BASIC 7.0 IRQ routines are inefficient. BASIC 7.0 spends far too much interrupt time simply determining that there is nothing to do. The existing functionality could be retained and overall system performance improved if an additional flag existed: is there any sprite or sound activity going on? This flag could exist in the two most significant bits of one byte. If the answer was "no" then the interrupt could be completed much sooner than under the current arrangement, which wastefully checks every single sprite and voice for activity before exiting. b) The existing routines maintain a separate timer of some sort for each voice and sprite and frequently do nothing but update various timers during an interrupt. In fact only one timer may actually be required: if the routines maintained a time-ordered priority heap, the only signficant question during most interrupts would be "is it time to do the action at the top of the heap?" (if the heap is empty the answer is obviously no, so exit the interrupt). The "actions" maintained in the heap could be anything at all. Maintaining existing functionality would require actions such as "update sprite position", "update voice frequency", and so on. Some actions would be self-reseeding, so that executing them also puts them back on the heap to be re-executed at some point in the future. 2) Additional Capabilities a) Lock sprite to joystick/game paddle so that it follows user input automatically. An "on buttonpress" keyword that, like "on collision", "interrupts" the normal BASIC statement execution flow to execute an "interrupt" routine (although "on collision" is in itself a pretty hairy proposition and this ability would create even more problems). b) A SIDPLAYER sound driver - perhaps even Craig Chamberlin's own current version, or an existing public domain version, if it seemed too much trouble to create a new SIDPLAYER compatible driver. This would provide access to a large library of existing music and encourage creation of even more with a sophisticated sound driver. On some level SIDPLAYER in ROM appears very similar to the provision of a sprite editor in ROM by BASIC 7.0, and possibly more useful. 3) Complete Redesign a) Examination of the BASIC 2.0 ROM of the C64 reveals a sharp division between the interpreter and the underlying hardware: anything which required hardware access was handled by calling a Kernel routine (including the attempt to seed the random number generator off the CIA timers). In contrast, for whatever reason, the BASIC 7.0 interpreter is riddled with instances of direct hardware access (mostly in the areas of graphics and sound, but also in seeding the random number generator). This is ugly, lazy coding with reduced readability and maintainability compared to the cleaner coding of the earlier BASIC 2.0 interpreter. Perhaps more importantly it decreases overall system functionality because the code cannot easily be used by anything but the BASIC interpreter itself. The functionality offered by BASIC's proprietary interrupt code is properly a system function, and should be accessible through the Kernel to anyone who wishes to use it. In general, any system ROM code which can directly manipulate system hardware resources ought to be made accessible through the Kernel or other jump table (as in the case of the BASIC 7.0 bitmap graphics jump table). In this view, any ROM-based bitmap, sprite, or sound drivers should be accessible via a jump table, and the BASIC interpreter should access them through this table. For example, the SIDPLAYER driver mentioned above might be used something like this: BLOAD or POKE a data table into memory, then issue a "PLAY
" command. This passes
to the sound driver, which then uses the data on an interrupt basis with no further interference from BASIC. Or perhaps the existing "PLAY" statement could be modified to convert its argument (a string) into a format acceptable to the sound driver, place it in a reserved buffer location, and then call the sound driver with a pointer to the buffer. Category: BASIC Graphics 1) Philosophy a) All primitive routines should be accessible through a ROM-based jump table. b) The high-level language need not necessary "work" the same way the underlying primitive routines do. For example, the primitive routines might require start- and endpoint coordinates to draw a line. The high-level language might maintain an implicit pixel cursor and require only an endpoint coordinate be specified by the user program, although it would of course give the underlying primitive routines what they wish to see. This approach would provide most if not all primitive services in ROM but leave the decision of what graphics "metaphor" to use at the high-level interface up to the language designer (eg., pen-on-paper, turtle graphics, and so on). The primitive routines should be efficient at dealing with hardware resources but the high-level routines should be efficient at dealing with the user. In particular, a high-level language need not force the user to provide long lists of parameters even if the underlying primitives require them. Long parameters lists often lead to much useless code repetition and are also hard to read, maintain, and remember. 2) Services Provided a) Obviously the primitive routines need to find their data somewhere, but this need not necessarily be accomplished through dedicated locations. Instead, an "init" routine might be provided with a pointer to the area in memory to be used to pass data back and forth (from the point of view of BASIC this might be dedicated or might simply be taken from the top of memory). The primitive routines would access parameter data as offsets within this allocated space. Except for possibly a pointer to the shared data space, the primitive routines would not use any zero-page space permanently. b) The foundation of bit-mapped graphics is the ability to set bits in memory to desired values. The graphics primitives must be able to do this but their own operational metaphor might be something other than changing one pixel in a rectangular array of pixels. Instead, many operations might be considered as combining two rectangular arrays, ie., as "bitblits". Such an ability is necessary anyway to provide for bit-mapped text; what is suggested is that other operations, such as "set point", "draw line", and "fill area" also function by combining rectangular pixel arrays (ie., patterns) with the existing bitmap. This would mean that many operations would require "source" and "destination" bitmaps. Some interesting effects are possible if the "source" bitmap is allowed to be a subset of the "destination" bitmap. c) A shape-drawing primitive, or a primitive which functions as an "interpreter". The operand would be a pointer to a data table which would be interpreted as a collection of other primitive commands, eg., set relative or absolute coordinate, draw point, draw line, draw text, fill area, and so on. From BASIC this might work by passing the address of a string to be treated as binary data, thus bypassing all kinds of interpretive overhead and greatly speeding up graphic functions. d) A Bezier or B-spline curve primitive built on top of the line-drawing primitive might overall prove as useful as a number of other common specialized-shape drawing primitives. Category: BASIC DOS Commands 1) Error-Checking a) Remove from the DOS command parser any parameter checking that the drive DOS does anyway. Two examples that come to mind are the way the RECORD# statement limits the parameter to between 1 and 254 and the way the drive specifier is limited to 0 or 1. These restrictions prevent easily making changes to the behavior of the commands later on, as for example if the range of parameters acceptable by the drive DOS increases. Another example would be the implementation of "logical" drive numbers at the Kernel level: a "virtual device" could be implemented which would associate logical drive numbers with physical drives, so that "0:" referred to device 8, drive 0 or any other device/drive combination specified by the user (it would be interesting to implement such a virtual drive as device 1, so that the default BASIC mass storage device was whichever drive/unit combination was currently associated with "0:", the default logical device). The current DOS parser would permit this but would only allow two physical drives to be considered, "0:" and "1:", whereas the virtual driver itself could easily permit up to ten, "0:" through "9:". 2) New Abilities a) Ability to specify a default mass-storage device, as opposed to the current hard-coded default device. Category: BASIC Math 1) Operations Supported a) Change XOR() from a function to an operator like AND, OR, and NOT, with the same precedence. Or go the other way and change AND, OR, and NOT into functions (anything to promote regularity among conceptually identical operations). b) Distinguish between logical and bitwise AND, OR, NOT, and XOR operations. One way to do this might be to handle bitwise operations as operators and logical operations as functions. Logical operations would only care about true/false (nonzero/zero) and not worry about whether the operands fit into two bytes. 2) Jump Table a) Don't tie operands to any particular memory locations. The "accumulators" could occupy dedicated zero-page space, but movement to and from the accumulators should be as free as possible. b) Additional entries for integer multiply and divide. These are such commonly required routines (even by the BASIC interpreter) that it would be handy to have highly optimized versions accessible to any user program. Category: Machine Language Monitor 1) Enhanced Abilities a) Forward and reverse scrolling through memory dumps and disassemblies. 2) New Abilities a) A simple disk monitor, ie., the ability to read and write single sectors from disk. Viewing and editing of such sectors are already enabled by the other monitor capabilities. b) Single-step execution. c) Set and clear breakpoints. d) Load sequential or user files into memory, so as to avoid having to write little BASIC programs to do so and then break into the monitor. Ability to replace all occurances of one byte in the file with another, or to delete all occurances of one byte (these are actually beginning to overstep the bounds of what a machine language monitor is supposed to do, but they would be handy utilities to have around). 3) New Jump Table Entries a) Return to monitor main loop. The present monitor code returns to the main loop by means of direct jumps. Currently user-written routines can intercept the main loop via its indirect command vector on page 3, but have no means of returning "safely" once finished since the address of the main loop is not on the machine stack or in the jump table at the start of monitor ROM. b) Access to monitor routines which perform common tasks as subroutines. This should enable use of routines like "transfer" to move data between memory banks in a way that may not be terribly efficient but is guaranteed to work. This would mean that the existing tendency to jump back directly to the main loop after concluding execution would have to be tempered. An alternative might be to make a routine like "move" available in the Kernel (it is a fairly common operation after all, albeit not strictly an I/O operation). Category: Screen Editor 1) Modified Jump Table Entries a) The window set routine should accept a pointer to the window definition in the X and Y registers and the bank number in A. If the carry is set, read the current window definition to the location specified. If the carry is clear, set the current window definition to the values from the location specified and put the cursor in the top left corner. Return carry set if the definition is in error (parameter too large or in conflict with another parameter). This puts the work where it belongs instead of the nonsense of having the calling routine (eg., BASIC's WINDOW statement) handle error-checking in an inefficient and duplicitive manner (does BASIC honestly need to check the screen size for each coordinate pair? And why should BASIC know anything about the hardware-determined screen size anyway?). The existing screen editor window set routine also currently does nothing to guarantee that the cursor is within the window when a new definition is set, which sometimes makes for curious and inconsistent behavior (particularly on the 80-column screen, and particularly if the window is not cleared at the time it is defined). This could be taken further - instead of simply the current window definition, use the entire current screen definition. This would allow more than the current 40/80 active/inactive screen dichotomy, permitting fast and easy switching between windows on the same screen (which could be treated as separate logical files). 2) New Jump Table Entries a) Turn programmable key expansion on/off. Probably best handled in the keyscan routine as a flag or pointer whose high byte is zero (else it points to the key definitions). BASIC would default to expansion on during direct mode, off during program mode, but could be overridden by something like "KEY ON" or "KEY OFF". b) Programmable keycode suppression. Possibly implemented as a pointer to a bitmap, so that if the high byte is non-zero the keycode detected is used as an index into the bitmap to determine whether the keycode can be placed into the keybuffer. The anticipated use would be mostly for screening user input. The ability to do this is to some extent already present, if the programmer is capable of supplying lots of alternative keycode lookup tables. Category: Kernel 1) Modifications to existing routines a) If a UART chip is not installed for RS232, the RS232 drivers should at least be rewritten to take advantage of the continuous-timing ability of the CIA chips. b) There should be flag to tell the NMI routine to ignore the interrupt and simply exit, or at least to ignore the RUN-STOP/RESTORE combination. This might be implemented by defining an additional bit in MSGFLG which tells the STOP routine to return "no" regardless of whether or not the key is actually being pressed. c) Rearrange the maskable interrupt service routine so that the check for BRK or IRQ branches when the BRK interrupt is found, rather than the IRQ interrupt. This costs nothing in space and saves a cycle every time the far-more-common IRQ interrupt occurs. d) Separate the disk sector-read ability from the rest of the BOOT routine and make a separate call out of it. Also add a disk sector-write ability, possibly flagged by the state of the carry flag. Parameters would be the track, sector, and drive number in the X,Y, and A registers (the unit number would probably be set by the SETLFS routine). e) Make the SETNAM and SETLFS file routines read/write based on the state of the carry flag rather than simply write-only. This would make tasks such as "What is the current input device number (so I know where to look for the rest of my data)?" a bit higher-level. f) Logical file support for screen windows as I/O devices. When a file is opened to device #3, the OPEN routine checks to see whether the filename parameter (from SETNAM) is null. If so, the default window is the entire screen. If not, the first four bytes of the filename determine the window boundaries (this works much like RS232 "filenames" currently work). If the operational unit is to be a screen instead of a window, any parameters missing from the filename string would be taken from the current screen definition. The distinction between this and the existing capability (which appears to simply use whatever window definition happens to be active) is that the window boundaries would be saved along with the other file parameters. This would allow several distinct logical screen files to be open at the same time, and switching between them could be accomplished simply by CHKIN and CKOUT. For example, an idea mentioned above for separating trace output from "normal" output could be implemented with this kind of file support. g) Take advantage of the time-of-day clock in one of the CIA chips to implement the timer support offered by the Kernel. This would be more accurate and require less system overhead during interrupts than the present purely software timer.