Memotech-Bill / PicoBB

BBC BASIC for Raspberry Pi Pico
zlib License
34 stars 5 forks source link

Support for Raspberry Pi Pico W? #8

Closed rtrussell closed 2 years ago

rtrussell commented 2 years ago

Are you planning to support the Raspberry Pi Pico W? I'm assuming that all this would involve is adding to the list of functions callable by SYS those necessary to access the network stack (and then somebody writing a library to make use of them, ideally compatible with the existing BBC BASIC 'socklib' libraries).

rtrussell commented 2 years ago

You have previously suggested using a structure for alignment. Does that mean that the start of the data storage of a structure is guaranteed to be at least 4-byte aligned? Is this documented somewhere?

Yes, structures are guaranteed 4-byte aligned (they have to be on many architectures). I'm not sure that it's formally documented, but it is implied by the comment "Note that the resulting structure is not guaranteed to be aligned" in the documentation of 'Assigning a pointer to a structure'.

rtrussell commented 2 years ago

Yes, structures are guaranteed 4-byte aligned (they have to be on many architectures).

Actually they are guaranteed 4-byte aligned in 32-bit implementations, and 8-byte aligned in 64-bit and ARM implementations. The code to ensure alignment can be found in bbexec.c:

    if (type & 0x10)
        while ((size_t)edx & 7)
            {
            ecx++ ;
            edx++ ;
            }

Of course this only works if the linker's alignment is at least 8 bytes as well, but all linkers I've tried align modules to 16-bytes.

rtrussell commented 2 years ago

Just seen your edit:

The only way I can see of obtaining alignment from within BASIC is to allocate byte arrays that are three bytes larger than the required data, and then use...

You can do that with memory allocated from the heap using DIM, but I discourage that usage because the resultant memory is unprotected from writing beyond the end (or before the beginning for that matter). There are three preferred methods of ensuring alignment in my versions of BBC BASIC:

  1. A 'global' (or PRIVATE) structure or array of structures allocated on the heap, which is guaranteed aligned.
  2. A 'local' structure or array of structures allocated on the stack, which is guaranteed aligned.
  3. A 'local' array allocated on the stack, which is guaranteed aligned (global arrays aren't aligned).

So you may sometimes see a LOCAL array created when there's no obvious reason for it being local; that may be to force it to be aligned. If you think it's strange for local arrays to be aligned but global ones not (I wouldn't blame you) it's because the stack must be aligned anyway - not least because in the x86 assembler versions the BASIC stack and the CPU stack (used for return addresses etc.) are one in the same!

Memotech-Bill commented 2 years ago

I have just pushed commits which provide a minimal demonstration of TCP connections.

It requires the attached programs to run on another machine (Raspberry Pi or desktop) to provide the other end of the connections.

First run "wifi_cfg.bas" to record your WiFi SSID and password in a file "wifi.cfg" on the Pico flash. (This prevents me accidentally leaving my details in the program source).

Then run "python_test_tcp_server.py" and "tcp_client.bas", or "tcp_server.bas" and "python_test_tcp_client.py [PicoW IP]".

Documentation of the new SYS calls is in "include/network.h".

UDP connections are still to-do. Also better examples (probably minimal HTTP client and server) which do not require custom target programs.

rtrussell commented 2 years ago

Then run "python_test_tcp_server.py" and "tcp_client.bas", or "tcp_server.bas" and "python_test_tcp_client.py [PicoW IP]".

Python?! What was wrong with using (or at least adapting) server.bbc and client.bbc or, for testing UDP sockets, lanchat.bbc? Especially given the hope that those exact programs will eventually run on the Pico W itself.

Memotech-Bill commented 2 years ago

Python?! What was wrong with using (or at least adapting) server.bbc and client.bbc or, for testing UDP sockets, lanchat.bbc? Especially given the hope that those exact programs will eventually run on the Pico W itself.

Probably nothing. I am more familiar with Python, and adapted the code from pico-examples. I didn't even realise that the BBC examples existed. Feel free to try those instead,

rtrussell commented 2 years ago

I didn't even realise that the BBC examples existed.

Well, you knew socklib existed, so you should have expected there to be example programs to illustrate its use!

Now you've been forced to adopt the approach of writing networking functions in C, to work around the absence of a working callback in BBC BASIC, are you matching them closely to what socklib needs? It would be nice, particularly from a performance standpoint, if a Pico version of socklib was little more than a trivial wrapper around your SYS calls, to ensure variable alignment etc.

(Incidentally I should have added to my earlier comments about alignment that of course the static integer variables A%-Z% are aligned, so in a library a reference like ^P% is always safe in a SYS call).

Memotech-Bill commented 2 years ago

Slow progress owing to my unfamiliarity with BBC BASIC.

I have pushed a work in progress:

rtrussell commented 2 years ago
  • IP addresses are now always passed by reference rather than value.

Sounds sensible. If you're passing IP address and port together you could use a structure to hold both (which as previously discussed guarantees alignment and cuts down on the number of parameters needing to be passed by SYS). You can declare a 6-byte structure member using ip&(5) or simply pass a 64-bit integer ip%% and waste two bytes.

IP addresses are passed into socklib as strings for the TCP routines, so I don't think there's any need for a change to support IPv6 at that interface. However the UDP routines take them as integers so that may need some thinking about (passing as a 64-bit integer is again a possibility, with some convention to distinguish IPv4 unambiguously from IPv6).

  • pico_client.bas works and will interact with server.bbc.

Excellent. At what point (and by whom!) are you expecting the Pico-specific code to be transplanted into socklib.bbc to allow the Pico to run the (hopefully) unmodified client.bbc, server.bbc and lanchat.bbc? Aren't you making extra work for yourself (or me!) by creating these intermediate Pico-specific examples rather than going straight for a library?

I appreciate the convenience of having a single program for test and debugging purposes, so what I usually do is to merge the test program (e.g. client.bbc) with the library (socklib.bbc) and once everything is working I separate out the library again. But at least it cuts out the necessity for an intermediate pico_client.

Memotech-Bill commented 2 years ago

pico_server.bas is now also working, but not yet pushed.

The original versions of client.bbc and server.bbc will never work on the Pico, they make use of OSCLI, which is not implemented. I must admit I don't yet understand that coding. I have yet to look at netchat.bbc.

In general I think there will usually need to be Pico specific code. There are the commands needed to establish the WiFi connection. I suppose that could go into PROC_initsockets, although you would then need to pass SSID, password and country code as parameters.

Once I have had a go at netchat to check that UDP is working, I may make a first pass at the library, but it will probably need your expertise to polish it.

rtrussell commented 2 years ago

The original versions of client.bbc and server.bbc will never work on the Pico, they make use of OSCLI, which is not implemented.

What?! You've disabled OSCLI? Why?

That's a major (and I would say unacceptable) limitation, because OSCLI is the only way in which 'variable' (i.e. known only at run-time) parameters can be passed to a 'star' command. Every version since 6502 BASIC 1 (1981) has had OSCLI for that reason, and even in that version you could achieve the same effect using CALL &FFF1.

I've checked and OSCLI works fine on the standard (non-W) Pico version available from my website, as it must because it's used in some of the supplied example programs (calendar.bbc, picowav.bbc, sortdemo.bbc).

I suppose that could go into PROC_initsockets, although you would then need to pass SSID, password and country code as parameters.

Adding them as extra parameters to PROC_initsockets would introduce an incompatibility with every program using socklib, which I don't consider to be acceptable. And it's not how any other system works, in my experience: those parameters are typically set once (when you initially configure the WiFi) and are then stored in a file so that you don't have to enter them again unless your WiFi setup changes or you want to connect to a different network.

In the case of the Pico, this would probably be a file (e.g. wifi.ini) in @usr$, created using a program like wifi_setup.bbc and read in PROC_initsockets. In an ideal world wifi_setup.bbc would be able to scan for networks and let you choose which to use.

Please don't take this the wrong way, but both these issues make me think we need to pause, take a step back and re-assess whether we're going down the right path.

Memotech-Bill commented 2 years ago

My unfamiliarity with BBC BASIC again.

OSCLI A statement which allows a string expression to be interpreted as an operating system command.

I mis-interpreted this statement as meaning that it would run a command on the host operating system, which we don't have on the Pico. I didn't actually try it.

I will take another look.

Memotech-Bill commented 2 years ago

I see that socklib currently uses two error codes

193 Too many open sockets 223 Invalid socket

Where are these defined and what new error values may I define?

rtrussell commented 2 years ago

My unfamiliarity with BBC BASIC again.

If you're unfamiliar with something, please don't jump to conclusions about it. References to the 'Operating System' in the context of BBC BASIC are to the BBC Micro's MOS, which contains commands like *exec*, spool, load, save** and many more (plus support for graphics, input-output etc.). Inevitably, when BBC BASIC is running on a non-Acorn platform these commands must be emulated, but they are still referred to as 'OS commands' because they are not part of the language.

This is what it says in the documentation: "On the BBC Microcomputer and Acorn Archimedes, star (*) commands provide access to the 'operating system'. These commands can either be 'resident' (i.e. permanently part of the operating system) or 'transient' (i.e. loaded when required). Modern operating systems do not have 'resident' commands in quite the same way, so BBC BASIC for Windows and BBC BASIC for SDL 2.0 implement their own resident commands as part of the application".

Whilst you may be able to manage without a very detailed knowledge of the BBC BASIC language (this is to a large extent out of your control if you are using my source code) I do think it is important to have a good understanding of the overall architecture, as represented in the diagram accompanying my GitHub project. Anything not in green here is not part of the BBC BASIC language and is therefore part of the (emulated) 'OS':

image

rtrussell commented 2 years ago

Where are these defined and what new error values may I define?

The error codes are defined here.

There's no formal mechanism for allocating new codes (too much admin!) so collisions are inevitable. You could trawl through all the existing libraries to see what codes have been used, but it's a lot of work for relatively little benefit. I would stick to codes >=128 if they are effectively reporting 'OS' errors.

Memotech-Bill commented 2 years ago

In PROC_initsockets I have defined:

194 No "wifi.cfg" file 195 Error initialising WiFi 196 Error connecting to access point

rtrussell commented 2 years ago

In PROC_initsockets I have defined: 194 No "wifi.cfg" file

I'm not sure I would class that as an 'error' (other than a "user error"!). Isn't it exactly what would happen if any program using socklib were run before the WiFi had been configured, and therefore quite an 'expected' situation?

My inclination might have been to write the WiFi setup program to be CALLable (basically that's just a case of being careful how it terminates) then in the event of the file being missing you could CALL that program from socklib.

If you don't like that idea you could at least consider changing the message from "No 'wifi.cfg' file" (how is the user supposed to know what to do to fix it?) to something more informative like "WiFi not configured, run wifi_setup".

Memotech-Bill commented 2 years ago

I have pushed commits with a Pico version of socklib in console/pico/examples/lib. I have included both source and tokenised versions.

Unmodified versions of both client.bbc and server.bbc seem to work.

lanchat.bbc does not work owing to commands unrelated to networking not being implemented on the Pico. I will see what I can do to make these issues non-fatal.

Memotech-Bill commented 2 years ago

What are your thoughts on making all the not implemented star commands into no-op on the Pico? At present they generate fatal errors.

By my reckoning these are: DISPLAY, EGA, FONT, GSAVE, HARDCOPY, MARGINS, MDISPLAY, NOEGA, OSK, PLAY, PRINTER, PRINTERFONT, SCREENSAVE, SYS

The strings would be matched, so that any mis-typed commands would still be detected, but the above listed command would just do nothing.

rtrussell commented 2 years ago

What are your thoughts on making all the not implemented star commands into no-op on the Pico? At present they generate fatal errors.

My objective is to make the Pico version of BBC BASIC as similar to the other Console Mode editions as possible, and in those versions commands which are not (and in general cannot be) implemented report an error. That way anybody running a program containing one of those commands is alerted that the program cannot work.

I can't see a benefit in allowing a program to continue running when it is inevitably going to fail later, in what could easily be a more confusing and hard-to-understand way. For example suppose a program contains a *GSAVE command to create a BMP file, if that command succeeds it can reasonably assume that the file will have been created and may not bother to test that. So if the file doesn't exist the program might just crash, or report an unhelpful error like 'Bad channel' when the problem was much more fundamental.

The only possible exception I can think of is when the command does something purely cosmetic (such as *FONT) but even then I think it is better to generate an error. In the specific case of the lanchat program it assumes it is running on one of the GUI versions of BBC BASIC because at the time they were the only ones supporting networking. It already tests @platform% to determine how to set the current font, so to adapt the program to run on the Console editions of BBC BASIC it's just a matter of extending that test.

Memotech-Bill commented 2 years ago

As per your earlier comments I was trying to run the unmodified program.

Would I be correct in thinking that bits 8-31 of @platform% are zero for console builds, and non-zero for GUI builds?

On a related subject, should INKEY$(-256) return "P" for the Pico builds as it is neither BB4W nor SDL?

rtrussell commented 2 years ago

As per your earlier comments I was trying to run the unmodified program.

When I said that I was hopeful that the Pico would run the unmodified programs I hadn't remembered that lanchat sets the font. Of course I don't expect a program that is written to run only on a GUI version of BBC BASIC to run unmodified on a Console version of BBC BASIC, that would be stupid. Until now, the only versions of (my) BBC BASIC which have supported network access have been the GUI versions, so changing the font to improve the user experience was a no-brainer.

Would I be correct in thinking that bits 8-31 of @platform% are zero for console builds, and non-zero for GUI builds?

Well, that was the case until recently but clearly isn't now, through your own request to be able to distinguish different Pico configurations. I posted to all the main BBC BASIC forums your proposal to allocate some of those previously-zero bits for that purpose, but there hasn't been a single response. That probably means there are no objections, but before finalising this extension the main documentation will need to be updated to formalise it.

On a related subject, should INKEY$(-256) return "P" for the Pico builds as it is neither BB4W nor SDL?

No, your lack of experience of BBC BASIC is showing again. The way the many different dialects of BBC BASIC (including those from Acorn, Matrix Brandy etc,) identify themselves and the platform on which they are running is by means of a hierarchy of information. The top level of the hierarchy is INKEY(-256), because that is implemented on all those different dialects. Once you have that you can drill down to more detailed information, but the way you do that depends on what INKEY(-256) returned because it is not standardised.

So you can think of the value returned from INKEY(-256) as a selector which determines what method to use to discover more information, and the value &53 or &73 indicates that the more detailed information is supplied in @platform%. Since the Pico version of BBC BASIC supports @platform% it should return &73. If @platform% indicates that it's an SDL2 version, you can drill down further by calling SDL-specific functions such as SDL_GetVersion().

Memotech-Bill commented 2 years ago

To provide an alternative test of UDP sockets I have written tftp_server.bas which can be used to transfer program or data files to or from the flash file system on a Pico W. This program, together with a few minor fixes has been pushed to GitHub.

To install a suitable TFTP client on your Raspberry Pi use sudo apt install tftp

Memotech-Bill commented 2 years ago

From the following statement from lanchat, perhaps @platform% for the Pico should be 8 not 6, since the Pico does not support *OSK.

IF (@platform% AND 7) >= 3 THEN *OSK ON

The way the many different dialects of BBC BASIC (including those from Acorn, Matrix Brandy etc,) identify themselves and the platform on which they are running is by means of a hierarchy of information.

On that basis, perhaps @platform% for all Pico variants should be a single fixed value (6 or 8) and I should create some Pico specific system variables which give details of the implemented network, sound and other capabilities.

Otherwise, if these details are to be recorded in @platform% I might suggest:

All platforms

Bits 24 - 31: Zero for console versions or GUI major version Bits 8 - 23: Platform specific Bit 7: Reserved Bit 6: 64-bit system Bits 0-5: Platform type

Pico specific

Bits 8-9: Network version (0 = None, 1 = GPIO, 2 = POLL, 3 = BACKGROUND) Bits 10-11: Reserved (Possible future network versions. There are a number of network capable Pico clones such as this or this). Bits 12-14: Sound (0 = None, 2 = Low quality PWM, 3 = Low quality I2S, 4 = High quality PWM, 5 = High quality I2S (TODO)). Bit 15: Reserved (Future sound options?) Bit 16: Console on UART Bit 17: Console on USB Bit 18: Flash file system Bit 19: SD card file system Bit 20-23: Reserved

Memotech-Bill commented 2 years ago

In preparation for merging the Pico W code into the master branch, I am thinking of creating two new folders in the repository: "console/pico_w" and "bin/pico_w". These will contain Makefiles with the correct default switches for building the standard Pico W versions. This will mean that just running make in these folders will build the correct versions.

Does this seem reasonable?

rtrussell commented 2 years ago

From the following statement from lanchat, perhaps @platform% for the Pico should be 8 not 6

I'm afraid the allocation of values to @platform% has been rather ad-hoc (basically just the order in which the various editions were developed). There might have been some value in allocating a bit to indicate 'desktop' versus 'mobile' (or if you prefer 'primarily keyboard/mouse driven' versus 'primarily touch driven') but it's too late to do that now.

I also haven't been consistent, when masking @platform% to exclude the '64-bit' bit, in whether to use AND 7 or AND &F (with only values up to 5 allocated it hasn't been relevant to date) so once a value greater than 7 is allocated that will matter.

So I don't think changing the Pico's value from 6 to 8 would be helpful. It could break some existing programs which use AND 7 because the Pico would then be recognised as Windows!

In the specific case of lanchat that's got to be modified for the Pico anyway, so it makes no difference.

Memotech-Bill commented 2 years ago

Network support now merged into master branch.