Tool : The BYG Shell

Posted by Papapower on Sun 12 May 2024

BygShell, getting back to system development on C64

The byg shell c64 tool

Look Ma, it's full of bugs

OK I've been busy working on a small side-project shell like program for the C64 lately that is now in the strange state between complete garbage and first bugged release.

I've decided to make the source code available on our github even if there is still a lot a work to do on the program and that there is no official release of it, I'll probably put a version on CSDB as soon as there are less bugs.

Source code repository is here

There are no plans at the moment to make anything usefull out to this, so don't hold your breath. On the other hand I'd be more than happy to hear your suggestions and feedback (already got some intersing ones regarding C128 and Ultimate 1541 APIs)

This is still a work in progress, and very much an experiment not targeted for real use for the time beeing, even if this will probably change as progess is made with the code. Expect a lot of bugs and don't rely on it to manipulate your files. Disclaimer : most of the comments and variable names in the source code are still in French for now, I'm progressively updating this to English.

And as usual, why ? Why not.

Some design objectives

First objective was to get back at some system coding as we haven't made a lot of progess on our next demo (there is still a lot to do on the graphics side and we're a bit stuck on that at the moment). Main idea was to implement some kind of system calls, BIOS like, with the vague objective to be able to use that for some tools later on. Then it quickly moved off tracks and into the rabbit hole with the addition of a user prompt...

For now, you're getting a shell-like environment where you can issue a selection of internal commands with a mostly unix-like syntax, provision for environment variables, external calls, basic script support and some sort of parameters expansion, but more on that later with a dedicated article and an improved release making it more usefull (who said less useless ?)

BIOS

1st design decision was to implement a call table with a simple macro in order to be able to call up to 128 system call entries. This is done with the following call, which has been later on encapsulated into some macros :

bios_exec:
    php
    asl
    sta bios_jmpl
    plp
    jmp bios_jmpl:(bios_jmp)

The accumulator is used as the main parameter, containing the BIOS entry number. The PHP / PLP sequence is used to preserve the carry value which is used as a modifier for some BIOS calls. Pseudo registers (see next) R0 and R1 along with register X are usually used to carry parameters for the BIOS calls.

bios_jmp is a 128 entries max words table containing jump addresses for BIOS functions. BIOS functions usually use the carry as a return flag : clear beeing OK, set beeing KO.

bios_jmp:
    .word do_reset
    .word do_pprint
    .word do_pprintnl
    .word do_var_set
    .word do_var_get
    .word do_var_del
    .word do_input
    .word do_var_count

An incompletely documented list of the BIOS entries is available in the github, I'll make those available here too probably in another article. We've got screen & keyboard I/O, variables, lists and string manipulation and conversions, parameters handling, disk and file I/O.

Pseudo registers

2nd design decision was to have a set of pseudo Zero Page 16 bits registers, along with some macros to make use of those, first use case beeing the ability to read / write streams of bytes in memory without having to re-write the same code all the time or use a too specific macro for that.

The priority was to make code easier to write vs having a more compact result code.

ZP bytes from $39 to $48 make for a good set of 8 16bit registers R0 to R7.

The first attempts resulted in a not very satisfying set of macros that were seriously lacking behind the objective of beeing able to write code more easily, so at some point some syntacting sugar glue was much needed as I didn't liked much the way the macro calls looked. A rudimentary python pre-processor for the source code was added in order to achieve a cleaner syntax, allowing to write things like that : operand [, ]

MOV R3, #$BEEF
MOV R5, R3
ADD R5, $4200
INC R3
PUSH R3

This also allowed the capability to write clean BIOS calls much like ARM software interrupt :

The SWI [, [, ]] syntax allows direct and easy BIOS calls

SWI bios.pprint, string_address
MOV R0, #100
SWI bios.wait

Pascal type strings and other data structures

I wanted to play a little whith Pascal type strings as I quite liked some capabilities of those for a long time, where the 1st byte of the string contains the string length and there is no string terminator value, so I added a macro to make it easy to insert those type of strings into the code along with a bunch of BIOS calls to manipulate those strings.

.macro pstring(string_data)
{
 .byte string_data.size()
 .text string_data
}

pstring("START")

BIOS string entries :

str_cat     : concatenate
str_cpy     : string copy
str_empty   : test for empty / spaces only string
str_expand  : variables expansion
str_pat     : pattern matching
str_cmp     : compare
str_chr     : char lookup
str_rchr    : reverse char lookup
str_lstrip  : suppress leading spaces
str_len     : length
str_del     : substring delete
str_ins     : substring insert
str_ncpy    : partial copy
str_split   : in place string splitting

Pointers to p-strings are used as paramaters for a lot of BIOS functions, such as the I/O ones.

    swi pprint, error_msg
    ...
error_msg:
    pstring("BOOM ! ERROR!")

After adding the p-strings, a natural candidate as a new data structure was the list of p-strings, so a plist data type was also added, along with some BIOS calls for easy manipulation.

list_add    : add pstring to list
list_get    : get pstring at position X
list_del    : delete pstring at position X
list_print  : print list 
list_size   : get number of elements in a list
list_reset  : resets an existing list

Environment variables

There is support for environment variables, along with the ability to expanse them on the command line or when printing a result. Quotes allow the input of values with spaces on the command line and single quotes prevent expansion. The variables are stored in memory in a data structure which is a list of p-string keyword / word pointer to data, data beeing stored in p-strings too.

To be continued...

More info to come in another article on the internals and more precisely on the shell commands and the way they are stored / launched / get their parameters.

The byg shell c64 hex dump

For now, provided that you're not scared and able to compile the whole thing (see asmbios.sh), feel free to play with it, in the meantime I'll add the binary to the github too.