====== MG's Apple ][ EhBASIC Port ====== I, as others before me, have been working on porting EhBASIC, by the late Lee Davison, to the Apple II. It's actually extremely easy to get it up and running in a minimalist fashion. What I have wanted (since childhood) is an easy to use/hack BASIC that I have the source code for, that is aware of things like MouseText, the ProDOS MLI, the Pascal 1.1 slot protocol, and other amenities. I wanted I/O to be a first-class citizen in the language, not a somewhat dirty hack that relied on the I/O hooks and literally ''PRINT''ing commands to the operating system. For those of us for whom it was our first programming language, we still love Applesoft. That being said, Apple's biggest updates to Applesoft since 1978 were support for lower-case entry and allowing the delete key on newer keyboards to be used as a backspace. There was pretty much nothing else that changed despite the evolving capabilities of the Apple II machines. | {{:projects:ehb0.png?direct&200|}} \\ Startup screen. | | {{:projects:ehb1.png?direct&200|}} \\ Some useful commands. | | {{:projects:ehb2.png?direct&200|}} \\ Woz's signature. [[https://www.youtube.com/watch?v=tp4gIoTgF-E|Credit]]. | ===== Design Criteria ===== In short, my design criteria for a "new traditional" BASIC for the Apple II were: * It should take advantage of the 65C02, MouseText, ProDOS, and other newer features when it make sense. * I/O commands should be first-class citizens. E.g. no more ''PRINT CHR$(4);"OPEN MYFILE"'' * This includes both slot-based character I/O as well as disk I/O. * Modest compatibility with Applesoft programs where it makes sense. When adding graphics commands, they should follow the Applesoft syntax. Where EhBASIC does something different but it is painless to make it behave like Applesoft, do it (e.g. in the ''INPUT'' command, don't display ''?'' if the programmer supplied a prompt). * Keep a 128K version in mind that can use Aux RAM. * It should be as welcoming to new programmers as Applesoft is. I don't quite have the skills to program a full BASIC interpreter, so EhBASIC was a natural fit for expansion due to its generous non-commercial license, well-commented and easy to understand source code, and ease of expansion. ===== Implementation Details ===== ==== What is Done ==== === Loader === The loader code is complete and features the following: * Sets up ProDOS memory map. * Moves EhBASIC and its Global Page to higher memory. * Makes sure a prefix is set to something sensible if not already set. * Scans the slots for Pascal-protocol firmware and collects their entry points. * Sets up EhBASIC keyboard and text screen I/O. * Cold-starts EhBASIC. * Loads and runs the startup program, if it was given and it exists. It defaults to ''EHSTARTUP'', but uses the well-defined method of accepting a startup program through a program launcher. === ProDOS Support === * Dynamic buffer management that can create fixed $400-byte buffers on-the-fly by moving string storage down in memory. Up to 8 buffers may be dynamically allocated. * $500 bytes of RAM is reserved for EhBASIC's own purposes. Mainly so that certain operations, such as ''SAVE'' continue to work even if all the buffers are used. * ''SAVE ""'' saves a program. Uses type $F8 (User 8) with aux type $EBA0. * ''LOAD ""'' loads a saved program. * ''RUN ""'' loads a saved program and executes it. * ''PREFIX [""]'' displays or sets the prefix. * ''ONLINE'' lists the online volumes. * ''CAT [""]'' gives a simple directory catalog. * ''RENAME "",""'' * ''DELETE ""'' * ''BYE'' quits back to ProDOS. * ''P8CALL ,'' makes arbitrary ProDOS calls. * ''P2B$()'' and ''B2P$()'' help with P8CALL * ''P2B$([,[,])'' - converts a Pascal string (length followed by bytes) to a BASIC string. Length is determined by the first value at addr, ANDed with and right-shifted by bits. * ''B2P$([,[,])'' - converts a BASIC string to a Pascal string embedded within a BASIC string. The value used for the length byte is the length of the source string, left-shifted by bits, and ORed with . * ProDOS errors are trapped, appropriate messages displayed for the most common of them, and an attempt to close all open files is made (this will change when error handling is introduced). === Pascal I/O Support === * Loader scans slots and places pascal entry points into the global page. * ''PR#''/''IN#'' work. * suffixing the slot # with a ! will force a re-init of the device (e.g. ''PR#3!''). * Monitor I/O is redirected to EhBASIC's I/O so calling monitor routines is routed to the selected devices. * If the prompt character changes, e.g. during ''CALL -151'', line feeds are added after each CR (Pascal I/O separates CR/LF functionality). === Text / Graphics Support === * High-bit-off ASCII is used. * Alternate character set (MouseText) is always on. * In 40-column mode, MouseText is accessible starting at ''CHR$(192)'' * In 40- or 80-column mode, MTEXT ON/OFF can be used, which turn on inverse and enable MouseText in place of upper case characters. * ''INVERSE'' and ''NORMAL'' behave as expected. ''FLASH'' is the same as ''INVERSE'' as flashing text is not supported with the alternate character set. * ''HOME'' clears the text screen. * ''SCREEN'' command: * Changes between text, lo-res, and hi-res. * Does not automatically clear the screen. * For HGR modes, can optionally make the drawing commands continue to draw on the previous page. * GR/Text page 2 can be displayed, but we always output to page 1 (for now). * ''CLS'' clears the current active screen. * ''TEXT'', ''GR'', ''HGR'', and ''HGR2'' all behave as Applesoft. * ''HGR0'' display full screen page 1. ''HGR1'' same as ''HGR'', ''HGR3'' mixed page 2. * ''COLOR='', ''PLOT'', ''HLIN'', and ''VLIN'' behave as Applesoft. * Additionally, ''PLOT'' can accept any amount of pixels in one command. E.g. ''plot 0,0,0,1,0,2,1,0,2,0'' plots 5 pixels. * ''HLIN'' and ''VLIN'' can take a comma instead of ''AT''. * ''HCOLOR='' and ''HPLOT'' behave as Applesoft. * ''HPLOT'' can take a comma instead of ''TO''. * ''PDL()'' and ''BTN()'' work for game I/O. * ''ERRNO(n)'' function where n=0 for line number of error, n=1 for BASIC error #, n=2 for Pascal I/O error #, n=3 for ProDOS error #. * ''GLOBAL(n)'' returns the Nth address of the EhBASIC global page. === Other New BASIC Commands and Functions === * ''BEEP [,]'' beeps the speaker. If the args are omitted, it produces the system bell. * ''TRY'' statement: * ''TRY [THEN ] [ELSE ]'' * ''ERRNO(1)'' is populated per the below table. * If ERRNO(1) is nonzero, ERRNO(0) is valid. * If ERRNO(1) is 25, ERRNO(3) is valid. * ''TRY'' ... ''THEN'' sometimes doesn't work well with ''PRINT'', ''INPUT'', and statements that don't evaluate expression arguments because they do not expect to be followed by ''THEN'' * E.g. ''TRY PRINT "FOO" THEN PRINT "BAZ" ELSE PRINT "BAR"'' will print "''FOOBAR''" and ''ERRNO(1)'' will reflect a syntax error. * For these cases, you can use ''TRY'' ... ''ELSE'', ''TRY'' with no clauses, or wrap in a subroutine (e.g. ''TRY GOSUB 1000 THEN'' ...). * Be careful nesting ''IF'' inside ''TRY'', that may cause unexpected behavior, especially if there is an ELSE clause. If necessary, a solution is to wrap the conditional in a subroutine. * ''TRY GOTO '' doesn't work. ''TRY GOSUB '' does. ^ ''ERRNO(1)'' Values ^^ ^ # ^ Error ^ | 0 | Success | | 1 | NEXT without FOR | | 2 | Syntax | | 3 | RETURN without GOSUB | | 4 | Out of Data | | 5 | Function Call | | 6 | Overflow | | 7 | Out of Memory | | 8 | Undefined Statement | | 9 | Array Bounds | | 10 | Double Dimensioned Array | | 11 | Divide by Zero | | 12 | Illegal Direct | | 13 | Type Mismatch | | 14 | String Too Long | | 15 | String Too Complex | | 16 | Continue Error | | 17 | Undefined Function | | 18 | LOOP without DO | | 19 | Undefined Variable | | 20 | Undimensioned Array | | 21 | Unimplemented function | | 22 | Illegal Argument | | 23 | I/O Error | | 24 | No Device Connected | | 25 | ProDOS Error (''ERRNO(3)'' contains ProDOS error number) | | 26 | File Type Mismatch | === Other features === * Pressing the tab key when at the beginning of an empty line in immediate mode will generate an automatic line number that is 10 higher than the last one. Note that due to the way EhBASIC flags immediate mode, it won't work right after a program ends, until something else is entered. ==== What is Planned, but Not Done ==== * ''PR#'' and ''IN#'' to files. * ''OPEN'', ''CLOSE'', ''WRITE'' (binary file I/O), ''READ#'' (read file like a DATA statement), ''READ()'' (binary file I/O), ''SEEK'', ''TELL()'', ''FLUSH'', ''PRINT#'' (text file I/O), ''INPUT#'' (text file I/O), EOF() - Operations on files. Slots will be able to be opened like files for some operations. * ''CREATE'', ''LOCK'', ''UNLOCK'', and ''CHTYPE'' for files. * ''SAVE'' and ''LOAD'' untokenized files option. E.g. ''SAVE "MYPROGRAM" AS LIST''. * ''POP'' -- dunno why EhBASIC does not have this. * ''ERROR '' -- cause error on purpose. * ''SCRN()'' and ''HSCRN()'' for seeing what pixel is displayed on the graphics screens. * ''PREFIX$()'' function for returning the current prefix. On GS/OS this should return the numbered prefixes as well. * Line editing and renumbering. * ESC-editing will never be supported due to the I/O expecting Pascal behavior. * That being said, I expect to be able to have a command such as ''EDIT '' that will pre-fill the input buffer with that line and let you edit it. Many optimizations of my initial implementations of functionality are needed! ==== Differences from standard 6502 EhBASIC ==== Not covered by the above: * ''INPUT'' with a supplied prompt does not display ''?'' prompt. * Mixed-case keywords supported. * Line editing supports forward arrow and the delete key. * Values below $20 are now additional command tokens (except $00), so the input routine rejects attempts to embed control characters via the keyboard. ==== What Will Not Be Implemented ==== * ''ON ERROR GOTO'' and ''RESUME'' * See ''TRY'' above. ===== Preview Releases ===== No source code yet, sorry. I do have a mirror of the repository I started with located [[https://github.com/mgcaret/6502_EhBASIC_V2.22|here.]]. Importantly, you should read the README and the EhBASIC manual. **Warning:** I //will// be adding more tokens, and this will cause you problems as certain tokens need to be in certain positions. Don't do anything serious with these preview releases. Files: ca. 02/12/2018 12:00 PST * Bug fixes in detokenization. * Bug fixes in ''TRY''. * ''TRY''...''ELSE'' now supported. {{ :projects:ehbasic.po.gz |Gzip-compressed .po file}} //Derived from EhBASIC.// {{ :projects:ehbasic.shk |ShrinkIt archive}} //Derived from EhBASIC.// ===== Example Programs ===== ==== Get the ProDOS Prefix into a Variable ==== In a low-level fashion, until I implement a better way: 10 a$=""+" ":REM the + is so EhBASIC won't store the string in program space 20 POKE SADD(a$),1:REM otherwise these pokes do bad things 30 DOKE SADD(a$)+1,$300:REM we'll use $300 as a buffer 40 P8CALL $c7,SADD(a$):REM call ProDOS GET_PREFIX 50 pf$=B2P$($300):REM convert result 60 PRINT "The prefix is ";pf$ ==== Get volume in slot 6, drive 1 into a Variable ==== 10 a$=""+" " 20 POKE SADD(a$),2:REM 2 params 30 POKE SADD(a$)+1,$60:REM unit # 40 DOKE SADD(a$)+2,$300:REM buffer (16 bytes) 50 P8CALL $c5,SADD(a$):REM ON_LINE 60 v$=B2P$($300,$f):REM convert string, only need lower 4 bits for length 70 PRINT "Volume in slot 6, drive 1 is ";v$ ==== Woz Signature ==== 10 HGR:HCOLOR=3:READ A,B:FOR I=1 TO 9:READ X,Y:HPLOT A,B TO X,Y:A=X:B=Y:NEXT I 15 READ A,B:FOR I=1 TO 16:READ X,Y:HPLOT A,B TO X,Y:A=X:B=Y:NEXT I 20 DATA 75,50,69,57,76,62,71,88,53,118,87,71,130,31,76,111,116,66,143,44,132,82, 123,83,108,95,104,104,112,107,130,94,133,80,154 25 DATA 73,146,89,158,88,162,94,139,141,122,160,119,156,139,126,194,85,232,64 [[https://www.youtube.com/watch?v=tp4gIoTgF-E|Credit]]. ==== Lo-res Heart ==== I forgot where I found this. If I find out, I will credit it here. 1 GR:COLOR=1:O=5:P=35:Y=2:FOR I=1 TO 4:READ X,Z:HLIN X+O,Z+O AT Y:HLIN P-Z,P-X AT Y:Y=Y+1:NEXT I 2 FOR I=1 TO 30:READ X:HLIN X+O,P-X AT Y:Y=Y+1:NEXT I:COLOR=15:HLIN 9,12 AT 5:VLIN 4,7 AT 10:HLIN 24,26 AT 5:VLIN 4,6 AT 25 32 DATA 5,11,4,12,3,13,2,13,2,1,1,1,1,0,0,0,0,1,1,1,1,2,2,2,3,4,5,5,6,6,7,8,9,10,11,12,13,14,15,16,17,18 ==== Hi-res Sine Wave ==== Don't remember where this one came from, either. 10 HGR:HCOLOR=3:LX=0:LY=80:FOR I=0 TO 279:A=SIN(I/8)*70+80:PRINT I,A:HPLOT I,A TO LX,LY:LX=I:LY=A:NEXT ==== Moire ==== My own variant of Brian's Theme. 100 GET a:HGR0 105 oc=c 110 X=INT(RND(0)*280):Y=INT(RND(0)*192) 115 c=INT(RND(0)*7)+1 120 IF c=oc THEN 110 125 HCOLOR=c 130 s=INT(RND(0)*4)+3 140 FOR i=0 TO 279 STEP s:HPLOT i,0 TO X,Y TO 279-i,191 145 NEXT 150 FOR i=0 TO 191 STEP s:HPLOT 0,191-i TO X,Y TO 279,i 155 NEXT 170 GET a 180 IF a=0 THEN GOTO 105 190 TEXT:HOME