Skip to content

Instantly share code, notes, and snippets.

@acmiyaguchi
Last active June 18, 2023 06:27
Show Gist options
  • Save acmiyaguchi/d897fcbb754a05f29faaa5d3fa414e7d to your computer and use it in GitHub Desktop.
Save acmiyaguchi/d897fcbb754a05f29faaa5d3fa414e7d to your computer and use it in GitHub Desktop.
SMART Response XE

SMART Response XE

I recently got my hands on a SMART Response XE, which is a defunct education device based on a ATMega128RFA1. It's basically a beefy arduino with a keyboard and screen.

Learning BASIC

The device that I bought came preloaded with a fork of Arduino BASIC. It took me an hour or two to get the basic control flow down, but its simpler than I expected.

The REPL flow is quite nice. You can swap between 9 banks of memory using mload {slot} and msave {slot} where slot is between 0 and 9. You can immediately execute an expression by typing it and hitting enter. You add code to your program by prefixing an expression with a line number. To delete a line, type the line number without any extra characters. Use list to see your source and run to run your program. These take a line number as an argument so you can skip around.

In a modern language like Python, you're given more control over execution flow of the program. In particular, IF expressions are limited to a single expression. If you want to run multiple lines in a branch of code, you must jump to a line to continue execution in that code block. The result of this is that line numbering becomes something that you need to mindful of during development.

There are observations or minor improvements that I would make on the implementation of BASIC on this device.

  • I would set enter to be the down or right key. I keep wanting to use the backspace key for it's intended purpose, and it's a little annoying to have to change it. A dynamic setting could be cool, built into the environment as a function.
  • When running LIST, hitting enter or down skips the last line in the entry, which means I can't see all the code I've written. This is a small off-by-one error. I think this is related to setting the position to the 7th row; if I do this and print a value, then we'll automatically scroll the buffer forward.
  • Hitting q when listing code should put me back into the the main interpreter. It would also be nice to see a pager of some kind at the bottom of the screen (like less), but it's by no mean necessary.
  • I would like a way to edit the current line using direction keys, without having to delete everything that I just wrote.
  • I would also like a buffer for history. Pressing up should bring up the previous statement, like sh/bash.
  • Hitting any of the buttons for math (=,+,-,x,/) is awkward. I need to have both fingers on the left-hand side of the keyboard. The bottom two keys of the display would be useful as extra modifier keys, since they're currently not doing anything.
  • meta+shift+v makes for a very unintuitive underscore. This should probably be under -. Actually shift+space is probably supposed to be the combo.
  • A HELP function might to start programming without having to dive into various docs. Alternatively, having a canonical reference with some examples would be cool. I'll try to include some useful examples in this doc, but it would be nice to have it built into the system.
  • The POSITION function takes column, then row. Which in retrospect make sense, given that origin is the top left and the arguments are x, y
  • There aren't any trigonomic functions to play with e.g. sin, cos, or tan, nor functions like sqrt. Implementing these from scratch seem little tedious when they can be wrapped from the c standard library. But I suppose implementing cosine from scratch might not be the worst thing to do.

links

1 REM guess a number game
5 LET actual=INT(RND*100)
6 LET tries=0
10 PRINT "guess a number"
20 INPUT num$
30 LET num=VAL(num)
31 LET tries=tries+1
40 IF num<actual THEN GOTO 100
50 IF num>actual THEN GOTO 200
60 GOTO 300
100 PRINT "too low"
110 GOTO 10
200 PRINT "too high"
210 GOTO 10
300 PRINT "correct"
310 PRINT "took "; tries; " tries"
# https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance
1 REM running stats
10 LET upper=100
20 LET n=100
30 LET k=0
40 LET sum=0
50 LET sumsq=0
60 PRINT "upper?"
61 INPUT upper$
62 upper=VAL(upper$)
70 PRINT "n samples?"
71 INPUT n$
72 n=VAL(n$)
100 FOR i=1 TO n
110 LET v=RND*upper
120 k=k+1
130 sum=sum+v
140 sumsq=sumsq+(v*v)
150 NEXT i
200 LET mean = sum/k
210 LET var = (sumsq - (sum*sum)/k)/(k-1)
300 PRINT "count: "; k
310 PRINT "mean: "; mean
320 PRINT "var: ; var
1 REM debug screen
2 CLS
5 LET x=4
6 LET y=30
10 FOR i=0 TO x-1
20 FOR j=0 TO y-1
30 POSITION j,i
40 PRINT "X"
50 POSITION 0,6
60 PRINT i; ", "; j
100 NEXT j
110 NEXT i
5 LET pi=3.14159
9 print "enter angle"
10 INPUT x$
20 LET x=val(x$)
21 x=x*pi/180
30 GOSUB 200
40 print x; ", "; cosx
50 GOTO 1000
200 REM cosine
210 LET x2 = x * x
220 LET x4 = x2 * x2
230 LET x6 = x4 * x2
240 LET f2 = 2
250 LET f4 = f2 * 3 * 4
260 LET f6 = f4 * 5 * 6
270 LET cosx = 1 - x2/f2 + x4/f4 - x6/f6
280 RETURN
2 CLS
5 LET pi=3.14159
9 k=32
10 dim res(k)
20 let s0=0
30 let s1=1080
# step is a keyword presumably
40 let ds = (s1-s0)/(k-1)
50 for i = 0 to k-1
60 let x = s0 + i*ds
# now we have to fix this because the approximation is only good close to 0
61 x = x mod 360
62 x = (x - 180)/180*pi
70 gosub 200
80 res(i+1) = cosx
90 next i
100 GOTO 300
200 REM cosine
210 LET x2 = x * x
220 LET x4 = x2 * x2
230 LET x6 = x4 * x2
240 LET f2 = 2
250 LET f4 = f2 * 3 * 4
260 LET f6 = f4 * 5 * 6
270 LET cosx = 1 - x2/f2 + x4/f4 - x6/f6
280 RETURN
# shift by 1, divide by 2, multiply by num of rows, integerize, and print
300 for j = 0 to 6
305 for i=0 to k-1
310 let v=res(i+1)
320 v=(v+1)/2
321 if v > 1 then v = 1
322 if v < 0 then v = 0
330 v=int(v*7)
335 if v <> j then goto 360
340 position i,v
# we clear lines in the middle rows, probably because of screen behavior
350 print "X"
# 351 pause 200
360 next i
370 next j
10 let t0=0
20 let n=100
30 t0=millis
40 for i=1 to n
50 print i
60 next i
70 let d = millis - t0
80 d = d / n
90 print d
@acmiyaguchi
Copy link
Author

acmiyaguchi commented Jun 10, 2023

Read through this blog post on cosine from scratch. I had ChatGPT write me a cosine implementation; it looks like the taylor expansion implementation is simpler to implement that I originally thought. https://chat.openai.com/share/c6b2a3a7-08c2-4ed2-a453-70a3f8b99f78

Taylor approximation:

cos(x) = 1 - x^2/2! + x^4/4! - x^6/6! + ...

Code (which needs to be simplified for entry):

200 REM Cosine function subroutine
210 LET x2 = x * x
220 LET x4 = x2 * x2
230 LET x6 = x4 * x2
240 LET factorial2 = 2
250 LET factorial4 = factorial2 * 3 * 4
260 LET factorial6 = factorial4 * 5 * 6
270 LET cos_x = 1 - x2/factorial2 + x4/factorial4 - x6/factorial6
280 RETURN

Finally a sample program:

10 REM Main program
20 DIM results(30)
30 LET pi = 3.14159265
40 LET start = 0
50 LET stop = 4 * pi
60 LET step = (stop - start) / (30 - 1)
70 FOR i = 0 TO 29
80     LET x = start + i * step
90     GOSUB 200
100    LET results(i) = cos_x
110    PRINT "cos(";x;") = ";cos_x
120 NEXT i
130 END

@acmiyaguchi
Copy link
Author

wiring

I got an SRXE Development Adapter from Brendan Lane's Tindie store. I'm looking at the pinout, and it look the ISP ports are mirrored 🤔

Finally got this working with this config:

[env:srxe]
platform = atmelavr
board = sparkfun_satmega128rfa1
board_build.mcu = atmega128rfa1
board_build.f_cpu = 16000000L
framework = arduino

; Using an Arduino as an ISP programmer
; 
upload_protocol = custom
upload_port = COM3
upload_speed = 19200
upload_flags =
    -C
    ; use "tool-avrdude-megaavr" for the atmelmegaavr platform
    ${platformio.packages_dir}/tool-avrdude/avrdude.conf
    -p
    $BOARD_MCU
    -P
    $UPLOAD_PORT
    -b
    $UPLOAD_SPEED
    -c
    stk500v1
upload_command = avrdude $UPLOAD_FLAGS -U flash:w:$SOURCE:i

Note that I had to set this up using the sparkfun board to use it correctly with the arduino environment.

@acmiyaguchi
Copy link
Author

Went through some grief trying to figure out why srxecore printing is slow slow.

I'm trying to figure out timing issues on redrawing the screen. The original port loops over 100 iterations of printing a variable with 138.04ms per loop. The new library loops over 10 iterations with 1005.5 ms per loops. This is significant, so refactoring we go...

description time (ms)
original 138.04
srxecore 1005.5
inlining putchar 970.9
removing _lcd_set_active_area 970.9
commenting even more logic 970.8

It turns out quite a bit of this testing was limited by platformio not updating the library after it's changed.
It'll pull the library once, and won't change it afterward.
I'll have to pin the library to specific versions at some point.

I went into some rabbit holes trying to figure out how to profile the code to get a sense where its slow.
The one that stuck out to me most was printing out the location of the program counter and counting that against the map of the compiled code.
This one would be cool if I only had serial access...

static volatile uint32_t pc = 0;
uint32_t getProgramCounter()
{
  asm volatile(
    "rcall get_pc\n"
    "get_pc:\n"
    "pop %A0\n"
    "pop %B0\n"
    "push %B0\n"
    "push %A0\n"
    "ret\n"
    : "=r" (pc)
  );
  return pc;
}

I also decided that I wanted to add a few more features to the basic implementation.

https://github.com/slviajero/tinybasic/blob/main/MANUAL.md

OPEN filename, <mode>
CLOSE <mode>
CATALOG <prefix>
DELETE filename
FDISK

// write a line
PRINT fp, string
// read a line
INPUT fp, varargs
GET fp, varags
PUT fp, varatgs

I dived into adding a filesystem into the device.
I spent about two days trying to figure out how to use littlefs.
When I added my original implementation into basic, there seemed to be some weird things going on that would cause it to hang during runtime.
I tried it again using a bare platformio configuration.

Debugging was taking a significant amount of effort, so with the help of a friend I got the ICSP interface soldered up so I don't have press down on the development board.

image

I put together a program to debug littlefs by defining my own LFS_TRACE macro that prints to the screen.
The srxecore library was causing issues here because all of the code is defined in headers causing functions to be declared multiple times.
I went through the process of rewriting the library to separate out the function definitions and static variables into code files so these wouldn't be recompiled every time it got included.
I also had to change my program so it was compiling c and not c++, which apparently mattered to littlefs functioning or not.

image

I managed to format the flash, create a file, read a file, and delete a file.
With this, I can start moving on to some other projects that build on these components.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment