Wednesday 23 June 2021

Nezha : RISC-V Linux SBC

The title of this post contains ample details to excite (me).  The "open source" architecture RISC-V is well on its way into the mainstream.  To be of general interest it needs to run Linux and a price point of $99 is well below previous development boards.

I ordered one as soon as I saw the indiegogo crowd funding page on 21st May.  Crowd funding was complete by 26th May, boards were shipped about 8th June and mine arrived yesterday.

Nehza has a similar form factor to a Raspberry PI and similar connectivity for HDMI, power, Ethernet, Wifi, Bluetooth, USB and Audio.  In a direct comparison RPI4 is faster, with more capabilities but that isn't the point.  Here we are running RISC-V not ARM.  Personally I feel this is an important moment in the evolution of systems.


I connected up the board, inserted the SD card provided and booted it up.  It started a Debian desktop and allowed me to signon.  Once ethernet was connected, I could happily SSH into the system.  This is perfectly what I hoped for.  I have a new (but familiar) world to explore.

Friday 18 June 2021

6502 : CC65 and C Programming

Until now I have used VASM to assemble my 6502 programs and it has done the job well.  I have noticed that there are a few 6502 C compilers and I recall from my bare metal programming that you (only!) need a stack, a library and some initialisation code for a C environment.  There appears to be a WDC compiler which isn't well used and CC65 which is popular and practical.  As a first step I will move from using VASM assembly to CA65, which is a part of the CC65 program suite.

CA65

As CC65 is available as a debian package and I use Windows 10 for my 6502 development environment I installed it under WSL (Windows Subsystem for Linux).  I can now take assembler source programs, which are written in Notepad++ and copy the to directories I use for WSL.  Assembly with CA65 starts with running ca65 at the command line with a few options specified.  In particular we have to specify --type as "none"; CA65 is happy to assemble for various microcomputers (C64, Apple ][, etc) but my home brew system has no extra requirements.  The output listing shows that it creates the same output byte for byte as VASM.  The output file is formatted for linking modules and libraries so although I have nothing to link in I also need to run the linker LD65.  So I replace my vasm command with:
    ca65 -l map -t none testbed.s    and
    ld65 -o testbed.exe -t none testbed.o
Now I can process and load the program via the Arduino Mega as previously.

CC65

Creating a C environment is somewhat more challenging.  Luckily Dirk Grappendorf has set this up for his 6502 which is very similar to mine and has been kind enough to create a github repo containing the code.  I downloaded the repo and set about making my own minimal code base.  My target is a "hello, world" program which outputs a screen message.


There are very few files in the solution:

cc65.lib is the most significant, it is the library of C functions needed for stdio and stdlib.

firmware.cfg defines the different areas of memory in the computer, ROM, RAM layout is specified so the compiler can do the hard work in placing files.

startup.s is the initialisation code which sets up the stack, initialises memory and calls main().

zeropage.s provides the layout for variables in the zero page.

acia.s contains the serial programs init, getc, gets, putc, puts which we will use for screen output.

interrupt.s contains skeleton interrupt processing code (which I am not yet using)

io.inc65 defines a long list of constants for ACIA, VIA and other devices.

Once I edited firmware.cfg for my memory configuration, io.inc65 for my acia addresses and acia.s for my serial port config (19200 baud) I could compile and link the program with a single command:
   cl65 -C firmware.cfg -m firmware.map -o main.exe main.c acia.s zeropage.s startup.s  interrupt.s cc65.lib

This created a 32KB file which can be burned into my ROM.  In fact I use my Arduino Mega to write ROM so I don't want the entire 32KB file.  My program is less than 1KB in size.  I amended firmware.cfg to "pretend" I only had 2KB ROM, which created a much more manageable file.  Amazingly, thanks mainly to Dirk, my C environment works and without too much trouble I can read and write the screen, here is the first test program. 😊😊😊😊😊


C lib

So far we haven't used any C library functions, our example uses Dirks acia_init,acia_getc, and acia_putc for IO.  In fact I can remove include statments for <stdio.h> and <stdlib.h> without issue.  Of course C programs without library functions are not particularly useful, we really want to have them available to us.  I tried adding the abs() function from stdlib.h and it worked fine.  I also successfully tested strcpy() from <string.h>.  

<stdio.h> is more difficult to test; I haven't "linked" my I/O routines into C streams yet and my hardware/software doesn't have interrupts setup so I wouldn't expect I/O to work.  I did hope that I could use the sprintf() function to format output for me and wrote a test program but it didn't work.  I will look into the problem in more detail when I consider cc65.lib.  In fact cc65.lib was provided by Dirk I need to look at how he created it or derived it from cc65 provided libraries.

My mechanism for loading programs into ROM  using an Arduino Mega sketch is wonderful, it is automated and requires no removal/insertion of chips.  Using C and library functions necessarily means that our programs are increasing in size - programs may be 4KB or even more!  The Arduino environment only has 8KB for variables so I had to use the PROGMEM mechanism which directs the compiler to store nominated constants in ROM only.  A 4KB program takes over a minute to load which is not bad really but I will need to look at speeding up the sketch.

SIM65

CC65 also contains a simulator SIM65, which you can use to test your C programs. It is easiest to use with programs containing standard C library functions (printf etc).  You can add in assembly language subroutines from C.

Of course, without the real hardware, low level "driver" functions don't have much meaning but you can "simulate" them.  For example my "term_puts" assembly routine could be replace by one which calls printf.
In fact most or all emulators have this problem, they can't exactly replicate my hardware.  If I aim to design my hardware / software to be similar to Apple ][, BBC Micro, C64 etc I could use their full emulators to test programs but even then my hardware will be quite different from the original product.

I am not sure if I will use this but it is another great tool to have available.



Tuesday 8 June 2021

6502 : Screen and Keyboard

 

I am thinking that the 6502 should have its own screen.  Traditionally an RS232 serial monitor would be appropriate for the time.  Back in the day I used a VT100 (text) and Tektronix 4025 (graphics) terminal attached to a PDP11 minicomputer.  I did consider buying a small terminal but, unsurprisingly, they aren’t made anymore.


I have an ESP32 and Waveshare 3.5” LCD screen which could be used.  Commands sent to the ESP32 could then be interpreted and the results displayed on the LCD screen.  Graphics would be done by commands as they were on Tektronix graphics terminals.  This is not similar to home computers which used memory map graphics.   ESP32 may be able to do cycle stealing graphics where it interleaves display and 6502 operations.  This would be aligned to the home computer ethos but quite a complex project.

I then considered how an RPI could be used.  Instead of connecting 6502 serial to the PC running putty we could connect it to my RPI 2B (called PI2)  which has a 3.5” screen attached.  I wasn’t sure how to connect my FTDI cable to the RPI.  I connected up with a loopback wire between RX and TX and then set about working out how to use it. 

Using a normal ssh session on PI2 I tried a utility miniterm (which seems to be python but works for me at the RPI command line) and it gave me a choice of ttyASM0 (console) and ttyUSB0  (serial port).  dmesg also shows ttyUSB0 being attached when I plug in the FTDI.  With miniterm using the serial port characters typed at the keyboard were echoed via the loopback wire.  I then setup miniterm to access the 6502 (miniterm /dev/ttyUSB0 -s 19200.  Rebooting the 6502 shows me output in the miniterm session.

PI2 has a Waveshare 3.5” screen directly connected to GPIO pins.  At installation the screen is configured so it is a console.  When PI2, which runs headless, is turned on boot messages are displayed.  On completion there is an autologin to user pi.  Connecting a USB keyboard to PI2 allows keyboard input at the console.  If I start miniterm at the console I see 6502 output on the LCD screen and can enter commands as 6502 input on the keyboard.  JOB DONE!

This is a brilliant solution, for a few minutes work we have a small self-contained text screen and keyboard for our 6502 system.  Purists will rightly argue that it is a modern powerful SoC acting as a dumb screen for a primitive system.  However a modern dumb terminal would be more expensive and have many components.  An old terminal would be real retro hardware, but we aren’t in that business.

miniterm doesn’t have many features and doesn’t support ANSI escape codes well.  I  tried minicom and putty but they didn’t work well for me but I settled on the linux utility screen which is simple but effective.

I can visit the screen topic later to see whether I can find a bit-map graphics terminal solution but for now I am happy with the system.

Monday 7 June 2021

6502 : Program Library

ROMLIB4

Our subroutine library ROMLIB3 is saved in memory at $E000.  There is nothing special about the code, we could store programs, program snippets or data in the library.  In fact I decided to store programs in ROMLIB4 starting at $E400.

As a first example I stored echo.s, my previous simple command line in ROMLIB4.  Following Dirk Grappendorfs monitor I added a command "jhhhh" to allow the user to jump in to a program.  For the first example "je400" causes the echo program to be invoked with its simple command line.  I added an "e" option to echo which exits back to cmdline with a "jmp $8000" causing it to reinitialise.

Previously I have written two diagnostic programs to show messages on terminal and LCD without using RAM.  I added these to the program library so they can be started from the monitor.  Alternatively they can be started by changing the contents of the reset vector at FFFC, FFFD or writing a short program, loaded at $8000 with a statment "jmp $E4xx".

As with the subroutine library I need to be very careful that labels are unique.  For programs there is more likely to be a clash as one program is often used as the basis for the next.  To make life simple I add a suffix, 1,2,3,4,... to the labels in successive programs to make them unique.

Instead of jumping to a program address from the monitor we would like to do a jump subroutine to an address in the subroutine libary with a call "shhhh", for example "sE1E2" to display registers and return to the monitor.  The "j" jump option used the "jmp (indirect)" opcode but the 6502 / 65C02 doesnt have a "jsr (indirect)" opcode.  I simulate this by putting a return address on the stack and jumping to the subroutine as shown below.

TESTBED

I can now add my cmdline.s to the program library (its start address is $E5D7).  I start a new program testbed.s which starts with the instruction "jmp $E5D7" which is a 3 byte opcode at $8000.  The program itself continues at $8003 and in fact I display "#" as a terminal prompt followed by options which I want to test.  One of the options is "e" which takes us back to the monitor.

Now when I compile and load the program I see my cmdline monitor.  I can then issue the command "j8003" which takes me to the "#" prompt and allows me to run functions I am testing.  Typing "e" takes me back to the monitor.

The testbed program is only about 100 bytes so it assembles really quickly.  The program logic is in ROMLIB4/cmdline.s and subroutines are in romlib3.  This gives me an excellent testing environment.







6502 : Reliability and diagnostic information

Reliability

Our environment for developing and running 6502 programs is still not reliable.  Since I put the hardware on a stand, physical connectivity has not been a problem.  I am past the stage where single stepping through 6502 programs looking at bus signals using the Arduino Mega is regularly required.  However when I turn the 6502 on, or reboot, the system doesn't always come up properly.

When you start the system  the Mega runs its program which typically stop the 6502 whilst it loads a program into ROM.  It is possible to use an external 5V power supply instead and disconnect both 5v and GND from Mega to breadboard.  This avoids any complications due to the Mega doing unexpected things.  It may be necessary to do a reset for the 6502 to boot normally.

I start a new terminal-based "monitor program called cmdline which has options for checking various system information.  Initially I set up power up self test (POST) subroutines for terminal and LCD but there isn't much testing the 6502 can do at start up without using terminal or LCD for output.  On the basis it is obvious if LCD and terminal are working I dispensed with POST.

cmdline has an "r" option which jumps to $8000 to reboot and initialise the system.  Interestingly this is very dependable, implying that startup problems are power or hardware related and the software is working just fine.

Diagnostic subroutines

Option "d" calls a subroutine to display register values: Accumulator, X, Y, P (status flags), S (stack pointer) and PC (Program Counter).  Program status flags are made available by pushing them on the stacvk (php) and popping them (pla) to the accumulator.  The TSX command provides access to the stack pointer.  The program counter is actually available st compile time and is retrieved by loading a label value.

The term-register subroutine is not very useful at the command line as it just shows values when the 6502 is at the monitor prompt; it is more helpful as a subroutine call when testing a program to print out register values.

Dirk Grappendorf has a monitor program which allows the user to display a range of memory addresses.  It is very useful for looking at page zero ($0000), stack ($0100) and working storage ($0200).  We implement "mhhhh" where "hhhh" is the start address to list.  It is pleasing that we can copy Dirks assembly code to convert hex numbers into our program - I don't want to have to write all the code myself.

I was also able to copy another subroutine PRIMM, courtesy of Lee Davidson at 6502.org.  This allows you to put display text in your program immediately after a subroutine call, which avoids situations where the text and code become separated when copying program snippets around.

One issue we face when using the 6502 system is that we dont know what program is running.  To improve the situation we display the program name and assembly time on the LCD and provide an option to display it on the terminal.
We capture the information  to be added within the formatHexProgram and forward variables which are copied into the writeROM sketch.  The sketch loads the information at $FFE0-$FFF7 in ROM so that subroutines can show details on screen or LCD.



Sunday 6 June 2021

6502 : Library and Shell User Interface

Library

We now have a number of subroutines for simple tasks which will be useful in a variety of programs.  It doesn't make sense to process them every time a program is assembled.  It is much better to save them permanently in an area of ROM, that is to say a library.

We start with a modified assembly procedure which saves object code starting at address $E000.  The library source is a set of subroutines (in alphabetic order) which I call ROMLIB3 (because it was my third attempt!).  Once these have been assembled and saved in ROM we look at the assembly listing to find the start address of each subroutine and add the address as a symbol in our program.  By convention I make the symbols upper case.  I can then delete the source subroutine from the program and replace the jump subroutine name with a symbol.


In this way we can use any or all subroutines in the library simply by adding a symbol table containing start addresses to a program. 

The upload sketch to store hex in ROM can only process about 30-50 bytes per second.  Our programs were increasing in size towards 1KB and we are now down to about 400 bytes so load time is halved to about 10s.

Initial library contents include:
Terminal i/o: getc, init, putc, putstr, hex_digit, hex_digits, registers (to show current contents)
LCD output: init_VIA, init_LCD,  clear, instruction (control command), putc, wait (until ready), hex_digit, hex_digits, putstr

We do have to be careful with our organisation when using libraries.  It is not permissable to change the contents of a library member as this will mean that the start addresses of all subsquent subroutines change and in turn the symbol table in each program needs to be amended and the program re-assembed.  Instead, if a subroutine must be altered, its labels are all prefixed with "OLD" and the new version of the subroutine added to the bottom of the list.  The latest program can then use the new version whilst previous projects remain unchanged.  

Shell

Rather than load a program which executes a single function it is useful to have a "monitor" program.  In fact this is a simple "shell" program such as bash, which is loaded when the user signs on and allows them to carry out various function.

Our shell starts of simple as shown in the picture below.  It shows a title on the terminal, in this case "Ava go" and a ">" prompt.  User input is a single letter, valid choices are l,n,o,p and Return.  The "l" option simply prints an upper case "L".  I find this very useful to check if input is working. "n" displays a hex string on the LCD screen, "o" is a memory dump progrm (wip), "p" tests the newline and "Return" shows a prompt.

We have now moved on from building hardware and writing programs which utilise it to a computer with a user interface and subroutines available to make the programmers life easier.

In the next episode we will expand our shell program to help us check on the current status of the computer.