Porting Richard Russell's BBC BASIC (Z80) to the TI-83+ and TI-84+ series calculators.

Moderator: benryves

benryves
Maxcoderz Staff
Posts: 3078
Joined: Thu 16 Dec, 2004 10:06 pm
Location: Croydon, England
Contact:

Here's an interesting speed comparison between BBC BASIC and the native TI-OS programming language. The test is the Sierpinski Triangle program in the guidebook (17-7):-

Code: Select all

``````:FnOff :ClrDraw
:PlotsOff
:AxesOff
:0→Xmin:1→Xmax
:0→Ymin:1→Ymax
:rand→X:rand→Y
:For(K,1,3000)
:rand→N
:If N≤1/3
:Then
:.5X→X
:.5Y→Y
:End
:If 1/3<N and N≤2/3
:Then
:.5(.5+X)→X
:.5(1+Y)→Y
:End
:If 2/3<N
:Then
:.5(1+X)→X
:.5Y→Y
:End
:Pt-On(X,Y)
:End``````
Translated into BBC BASIC, it looks like this:-

Code: Select all

``````   10 REM Sierpinski Triangle by Texas Instruments.
20 REM Taken from the TI-83 Plus Guidebook.
30 CLG
40 X=RND(1):Y=RND(1)
50 FOR K=1 TO 3000
60   N=RND(1)
70   IF N<=1/3 THEN X=.5*X : Y=.5*Y
80   IF 1/3<N AND N<=2/3 THEN X=.5*(.5+X) : Y=.5*(1+Y)
90   IF 2/3<N THEN X=.5*(1+X) : Y=.5*Y
100   PLOT 69,X*96,64-Y*64
110 NEXT K``````
On a regular TI-83+ (6MHz) the TI-OS program takes 7 minutes and 8 seconds to complete. The BBC BASIC program takes 2 minutes and 21 seconds. That's quite a speed difference!
Here it is running on an emulated 83+SE at 15MHz.
tr1p1ea
Maxcoderz Staff
Posts: 4132
Joined: Thu 16 Dec, 2004 10:06 pm
Location: I cant seem to get out of this cryogenic chamber!
Contact:

Yay, its own forum!

I always liked the Sierpinski Triangle, and wow! Thats a huge speed improvement!
"My world is Black & White. But if I blink fast enough, I see it in Grayscale."

benryves
Maxcoderz Staff
Posts: 3078
Joined: Thu 16 Dec, 2004 10:06 pm
Location: Croydon, England
Contact:

A slightly optimised version takes it down to 1 minute and 56 seconds.
BBC BASIC passes control to the host interface when it encounters GCOL so I've managed to exploit that to add an extra statement - GCOLPAT. You can pass it a pointer to an 8-by-8 pixel pattern in memory and it will use that in place of the foreground/background dither patterns in subsequent plotting operations that fill regions of the screen (you can reset it to normal by invoking GCOL again or passing 0/FALSE to GCOLPAT. The code for the above example is here. This could allow for some interesting special effects!

Edit: I've just reshuffled the app around a bit and added *EXEC. This reads console input from a text file; this could be useful with external text editors. You could type up your BBC BASIC program in any on-calc text editor, then *EXEC then SAVE it in the BBC BASIC environment to convert it (tokenise it) to a BASIC program.
In the image a file is opened for writing with OPENOUT, three lines of program text are PRINT#ed to it, then it is *EXECecuted to convert its text to a BASIC program. You could also use this for runtime code generation or similar by storing the text to a temp file first.
Timendus
Calc King
Posts: 1729
Joined: Sun 23 Jan, 2005 12:37 am
Location: Netherlands
Contact:

Ben... this is insane!
A clock, fill patterns, all these features... and the SPEED... I envy your brain
http://clap.timendus.com/ - The Calculator Link Alternative Protocol
http://api.timendus.com/ - Make your life easier, leave the coding to the API
http://vera.timendus.com/ - The calc lover's OS
tr1p1ea
Maxcoderz Staff
Posts: 4132
Joined: Thu 16 Dec, 2004 10:06 pm
Location: I cant seem to get out of this cryogenic chamber!
Contact:

Ive been trying to devise ways to covertly thieve brain power from Ben. So far all my efforts have proven unsuccessful :S.

I am confident that this project will have the added side effect of succeeding BASIC libs in that the speed gain should be a fair bit better. Plus the familiar/friendly BBC BASIC syntax and bens sweet editor are all pluses in my eyes .
"My world is Black & White. But if I blink fast enough, I see it in Grayscale."

benryves
Maxcoderz Staff
Posts: 3078
Joined: Thu 16 Dec, 2004 10:06 pm
Location: Croydon, England
Contact:

Cheers for the kind comments chaps.

I've been working on triangle filling over the weekend. I initially tried to do something clever using the line clipping code qarnos wrote, but couldn't get good results from the "clever" method so have gone for sluggish "stupid" method instead.

Triangles Demo at 15MHz
The requirements of the triangle filler are as follows:
• All three vertices are specified using signed 16-bit coordinates.
• The triangle is clipped to the user-definable graphics window - 8-bit coordinates indicating a top-left and bottom-right corner.
• Each scanline must be filled once (and once only - no overdraw) by calling HLineFill by qarnos with a=y, b=x1, c=x2 and b≤c.
I use Bresenham's line drawing algorithm to trace the three sides of the triangle, storing the minimum X and maximum X for each scanline (think of a line where its |dX|>|dY| - more than one pixel would be plotted per scanline, else the line would have gaps). Then the triangle is filled in two halves, from A.Y to B.Y-1 then B.Y to C.Y. For each scanline the minimum and maximum X component is found from the two sides and used as the bounds of the filled scanline.

This is rather slow for a number of reasons - Bresenham's line drawing algorithm is implemented in full 16-bits (using both "regular" registers and the shadow registers), and the whole triangle is scanned from top to bottom. I may add a special-case 8-bit version of the tracer (when both ends of a edge fit into 8-bit coordinates) but for the moment I'm going for robustness over speed, and only having one code-path makes life easier!
tr1p1ea
Maxcoderz Staff
Posts: 4132
Joined: Thu 16 Dec, 2004 10:06 pm
Location: I cant seem to get out of this cryogenic chamber!
Contact:

Looking great ben, by the way that code listing script you have is really nice .
"My world is Black & White. But if I blink fast enough, I see it in Grayscale."

benryves
Maxcoderz Staff
Posts: 3078
Joined: Thu 16 Dec, 2004 10:06 pm
Location: Croydon, England
Contact:

Thanks. The slightly garish colours are courtesy of the Windows version of BBC BASIC.
I've added rough-and-ready parallelogram filling as an extension of triangle filling. Given point1, point2 and point3 the fourth point is calculated by point3-point2+point1.

As it's drawn as two triangles there is a line of pixels that doesn't appear to be drawn between points 1 and 3 in EOR/inverse modes thanks to overdraw (draw the same pixel twice in EOR/inverse mode and you're back to where you're started). That needs to be fixed.
benryves
Maxcoderz Staff
Posts: 3078
Joined: Thu 16 Dec, 2004 10:06 pm
Location: Croydon, England
Contact:

I've started hacking together support for some of the BBC Micro OS call routines. Naturally there are some differences in implementations (as this is a Z80 there isn't an X or Y register) but the addresses and functionality are similar.

As an example, in BBC BASIC you can output a byte to the VDU driver (and so output text, define viewports, draw shapes and so on) by using the VDU command, like this:

Code: Select all

``VDU 16``
(VDU 16 = clear graphics screen - ie, what CLG normally does). This is good, but such functionality would be nice to have in assembly too so your snippets of inline Z80 code can also output text, graphics and so on. For this purpose, there's a routine at address &FFEE called OSWRCH that outputs the byte in the accumulator, so the above could be replicated with the following:

Code: Select all

``A%=16 : CALL &FFEE``
BBC BASIC lets you intercept CALLs to the &FF80..&FFFF range (where the BBC Micro routines reside) and redirect them to somewhere more appropriate. However, this functionality does not work in the assembler - and a CALL &FFEE will crash the calculator as &FFEE on the protected RAM page 0. Therefore, all OS calls are offset from &FF80..&FFFF to &4080..&40FF, and so

Code: Select all

``[ LD A,16 : CALL &40EE ]``
would be the equivalent inline assembly version.

There are a large number of useful routines (setting clocks, getting cursor positions, reading lines of text, handling files, finding out memory ranges). Some are available in BASIC, and some can be used to perform tricks in assembly that aren't possible in BASIC directly.

An example of this is the following assembly program. It can be used to work out the graphics origin. It works by moving the graphics cursor to (0,0) then reading back the current cursor position via the OSWORD OS command. OSWORD is a generic routine that takes a routine number in A and a pointer to a parameter block in HL. In this case A is &09, which means "read the last two graphics cursor positions", and it outputs the cursor positions as an 8-byte block .

Code: Select all

``````   10 REM OS call routine constants
20 OSWRCH=&40EE
30 OSWORD=&40F1
40 :
50 DIM GetOrigin 90
60 FOR pass%=0 TO 1
70   P%=GetOrigin
80   [
90   OPT pass%*2
100   ;
110   ; Check there are 2 arguments
120   LD A,(IX+0) : CP 2 : RET NZ
130   ;
140   ; Check they are both integers
150   LD A,(IX+1) : CP 4 : RET NZ
160   LD A,(IX+4) : CP 4 : RET NZ
170   ;
180   ; MOVE to the origin by invoking VDU 25,5,0;0;
190   LD A,25 : CALL OSWRCH
200   LD A,5 : CALL OSWRCH
210   ; Output 4 zeroes to move to (0,0).
220   LD B,4
230   .lp XOR A : CALL OSWRCH : DJNZ lp
240   ;
250   ; Invoke OSWORD &0D to read last two cursor positions
260   LD HL,PrevX
270   LD A,&0D : CALL OSWORD
280   ;
290   ; Now we know the origin we need to output it.
300   LD L,(IX+2) : LD H,(IX+3)
310   LD DE,(CurrX) : CALL OutputCoord
320   LD L,(IX+5) : LD H,(IX+6)
330   LD DE,(CurrY) : CALL OutputCoord
340   RET
350   ;
360   ; 8 bytes of storage for cursor positions
370   .PrevX : DEFW 0 : .PrevY : DEFW 0
380   .CurrX : DEFW 0 : .CurrY : DEFW 0
390   ;
400   ; Outputs 16-bit value DE to 32-bit variable at HL
410   .OutputCoord
420   LD (HL),E : INC HL
430   LD (HL),D : INC HL
440   SLA D
450   SBC A,A
460   LD (HL),A : INC HL
470   LD (HL),A : RET
480   ]
490 NEXT pass%``````
You could invoke it like this (note that VDU 29 sets the graphics origin):

Code: Select all

``````VDU 29,48;-32;
CALL GetOrigin,x%,y%
PRINT "Origin = (";x%;",";y%;")"``````
I appreciate this probably means nothing to people not familiar with BBC BASIC..!
benryves
Maxcoderz Staff
Posts: 3078
Joined: Thu 16 Dec, 2004 10:06 pm
Location: Croydon, England
Contact:

Wstfgl. After all that time, odd bugs have started creeping in, usually resulting in a crash if certain operations are carried out in sequence (for example, mixing * commands with file manipulation). When switching between BBC BASIC and the TI-OS registers are backed up like this:

Code: Select all

``````FunctionA:
call SaveBasicRegisters
; ... stuff ...
call RestoreBasicRegisters
ret

FunctionB:
call SaveBasicRegisters
; ... stuff ...
call RestoreBasicRegisters
ret``````
If FunctionA (which may be the OSCLI handler for * commands) calls FunctionB (which may be related to file I/O) it would end up saving the TI-OS mode registers to the BASIC register save slot. After inserting a counter to ensure that you don't accidentally save or restore too many times the nature of the crashes changed somewhat.

Another potential source of errors is an imbalanced stack.

I've removed every function that used SaveBasicRegisters or RestoreBasicRegisters and the calculator no longer crashes, but this has removed a lot of functionality (namely anything that works with files). Hrm. This is quite a significant set-back.

Edit: It seems to be related to the file I/O routines. BBC BASIC lets you open a file for read and/or write access, returns a handle, then gives you full random access to it. This makes the TI-83+ code rather complex, eg writing to the end of a file opened for writing should make it grow. There's some rather messy code in there to deal with "dirty handles" to cope with RAM being shuffled around by the TI-OS file editing routines and the like. It may need a full rewrite...

Edit 2: Seems like it could be related to enlarging files. The following program locks up the calculator:

Code: Select all

``````   10 h=OPENOUT"TESTFILE"
20 FOR i=1 TO 100
30   PRINT "Test #";i
40   PRINT#h,"The quick brown fox jumps over the lazy dog."
50 NEXT i
60 CLOSE#h``````
This usually crashes after about 5 iterations. Changing "TESTFILE" to "PIC1.PIC" (the fixed-size Pic1 variable) hits iteration 17 before the safe BASIC Disk full error is triggered. Working on it...

Edit 3: I think I found it. After a lot of rewriting to no avail, I've removed a bcall from the ISR and it all started working again! (Even with a dummy bcall, ie to a routine that just rets, causes the weird crashes).

In the meantime I rewrote the entire Windows-based tokeniser (thing that converts text to BBC BASIC programs and wraps them in a .8xp) and that has ironed out all its bugs.
driesguldolf
Extreme Poster
Posts: 395
Joined: Thu 17 May, 2007 4:49 pm
Location: \$4080
Contact:

I feel your pain... If you just added some code and it crashes, then fixing the problem is trivial.
But if something appears to be working fine, you move your focus to other things and suddenly you get weird errors? Those are the most annoying bugs to find. It gets harder as the complexity if the project increases.

I sure wish you good luck
benryves
Maxcoderz Staff
Posts: 3078
Joined: Thu 16 Dec, 2004 10:06 pm
Location: Croydon, England
Contact:

Thanks for the support. It seems to have been fixed now, at any rate.
A couple of glitches in the triangle filler are causing me grief now. They seem to be related to calculating which scanlines to fill between.

On the features front, I've added a break/reset key. Holding On for five seconds forces BBC BASIC to restart.
The On key triggers an Escape error normally. If there is an error in your error handler this can cause real problems; in the above example the error handler in line 10 doesn't bail out and runs into line 20 (which contains an error) causing an infinite loop. Pressing On to terminate execution doesn't work, as that itself triggers an error!

Once BBC BASIC has been restarted your program is lost, but as long as you don't enter any new program lines you can retrieve it with the OLD keyword.
tr1p1ea
Maxcoderz Staff
Posts: 4132
Joined: Thu 16 Dec, 2004 10:06 pm
Location: I cant seem to get out of this cryogenic chamber!
Contact:

Ahh goodo, safety first! I was worried as i read '...your program is lost' but i see the life saving OLD is there to save the day .
"My world is Black & White. But if I blink fast enough, I see it in Grayscale."

benryves
Maxcoderz Staff
Posts: 3078
Joined: Thu 16 Dec, 2004 10:06 pm
Location: Croydon, England
Contact:

When I say "lost" that's the copy of the program that had been loaded into BBC BASIC's memory, not the program variable (if it had been saved previously). That means that if you have a program variable in RAM and LOAD it into BASIC there are two copies; normally you'd run BBC BASIC on a computer with some sort of external filing system (eg disc, tape or network). As the host interface transparently handles reading files from the archive you can store all your programs there, at least.

OLD works if you exit the flash app and restart BBC BASIC, as long as you haven't overwritten the RAM that BBC BASIC previously used to store the program.

Safety doesn't really go hand-in-hand with power, so you'll still be able to crash the calculator by using the indirection operators to overwrite important memory, CALL a routine you shouldn't or write a buggy assembly routine - but it's good to try and keep the high-level functionality as safe as possible.
benryves
Maxcoderz Staff
Posts: 3078
Joined: Thu 16 Dec, 2004 10:06 pm
Location: Croydon, England
Contact:

A post on RevSoft made me check and see if it was possible to use BBC BASIC's inline assembler to assemble "regular" assembly programs on-calc.
The BBC BASIC program creates an assembly program. A few nifty features of BBC BASIC allow this to be possible - usually programs are assembled directly at the specified origin P% but one of the OPT bits can be used to assemble it elsewhere (at O%). Once it has been assembled *SAVE is used to save the assembled file (in memory) to a program variable. A little clunkier than a dedicated on-calc assembler, but this may show how flexible BBC BASIC is.

I've rewritten all of the OS-level commands (they were implemented slowly, buggily and inefficiently).
One that's been missing is *CAT (aliased to *DIR and *.) to list calculator variables. By default it outputs the name of every available variable, but you can pass it a pattern with wildcards which can be used to filter the names (eg *CAT F* to list all variables starting with the letter F or *.???.VAR to list every AppVar with a three-lettered name).

I've also optimised the block copy and save operations (operations that save or load an large block of memory to/from a file) with special cases to handle variables in RAM (LDIR) and a simplified loader for variables in ROM. The result is instantaneous loads for variables in RAM, and loads from ROM are not much slower.

The Escape condition is now maskable, and you can prevent pressing On from triggering an error with the *ESC OFF command.

I think I fixed the triangle filling bug from the earlier post, too.