Guidance to programmers

From Spectrum hardware wiki
Jump to: navigation, search

This is mainly aimed at assembly language programmers, but if you're using C, you should still find this worth reading. BASIC programmers can safely ignore this section, since BASIC extensions will take care of all of this for you.

There are a few general principles that'll (hopefully) avoid any pain if you follow them, when accessing the hardware. This page outlines them, and gives some reasons why you should do certain things.

The Spectranet hardware is arranged so that memory is in 4 kilobyte pages, and these get mapped into the lowest 16k of the Spectrum's memory map - which is the only place external devices can map memory. Two of the pages are fixed, to guarantee there is always access to certain things. The first 4k of flash ROM is permanently mapped into the first 4K of the memory map. This piece of ROM contains the socket library, initialization routines, some general purpose screen and keyboard I/O routines, and the trap entry points. The last 4K (0x3000-0x3FFF) is permanently mapped to the lowest page of the Spectranet's RAM. System variables are stored here. Not all of this RAM is used - some of this fixed page is available to you. The middle two pages are known as page area A (0x1000-0x1FFF) and page area B (0x2000-0x2FFF). Typically, program code in other ROM pages gets paged into paging area B and run from there, and RAM (whether this is ethernet hardware buffers, or the Spectranet's static RAM) gets paged into area A. However, any page from any memory can be put into either paging area A or area B - and this may be necessary - for instance, to copy data from the ethernet buffers to static RAM will require paging area B to be occupied by the static RAM (the library code always uses page area A for ethernet buffers).

To avoid having to reassemble programs every time there's a firmware update, all the public entry points to the library code (which resides in 0x0000-0x0FFF) has an entry in a jump table. The jump table is copied to 0x3E00 (which is in the fixed static RAM page), and the addresses within this table will remain stable over ROM revisions. The table is in RAM to allow flexibility: it allows programmers to override functions in ROM, by modifying the jump table to call their code instead of the normal ROM code - this can be used to completely replace functions, or just intercept the call and provide a small amount of additional functionality. The jump table itself is accessed by two CALL entry points (one at 0x3FFA and another at 0x3FFD) which causes the Spectranet memory to get paged in. You should never need OUT instructions in your code to access any Spectranet function. (More details on the call entry point to use is contained in the documentation for each function call. 0x3FFA is known as HLCALL, and calls the address in register pair HL, and 0x3FFD is known as IXCALL, jumping to the address in the IX register).

With this background, there are some general principles to bear in mind:

  • Avoid using OUT instructions (especially while the Spectranet is in the prototype stage) to directly perform Spectranet operations unless there's a really, really good reason. If you just want to cause Spectranet memory to page in without actually running a Spectranet function (for example, to access the additional memory), use CALL 0x3FF9 instead - this causes a page in and then executes RET immediately. Similarly, to page out, CALL 0x007C, which causes the hardware to unpage at a RET instruction.
  • To page in different pieces of memory into paging areas A and B, use the functions SETPAGEA and SETPAGEB, rather than using OUT instructions. This will keep the system variables up to date with the hardware, and insulate you from any hardware changes that may occur in the future.
  • If you want to avoid the overhead of the dispatch routines (via the CALL 0x3FFA and 0x3FFD entry points) when you have many Spectranet calls lined up (and won't be needing the ZX ROM for a while), you can CALL 0x3FF9 to cause a page in, then CALL the jump table directly. For example:
; Going via the dispatcher.
.poll
    ld a, (sockethandle)
    ld hl, POLLFD ; Call the poll routine
    call 0x3FFA   ; HLCALL entry
    jr z, .poll
.dataready
    ...rest of code

Could be changed to this, to save a few dozen cycles that are involved with setting up the page in and page out with each call:

; Stay paged in while polling
    call 0x3FF9    ; cause a page in
.poll
    ld a, (sockethandle)
    call POLLFD
    jr z, .poll
.dataready
    call 0x007C    ; cause a page out
    ...rest of code

You should also use the symbolic names HLCALL (instead of 0x3FFD) and UNPAGE (instead of 0x007C), of course - these were just elided here so you can more easily see what's happening.

  • If you are writing ROM code, there is no need to cause a page-in - simply call the jump table directly (as in the above example, but without the call to 0x3FF9) since if you're running in ROM, you'll already be paged in.
  • A mechanism is provided to call the Spectrum ROM while the Spectranet ROM is paged in. If you're familiar with programming with the interface 1, the mechanism works in exactly the same way: do an RST 0x10 with the following two bytes being the address of the function you want to call in the Spectrum ROM. For example, to set the current output stream to the main part of the screen:
; The code you might normally write
    ld a, 2
    call 0x1601
; The way to call the routine at 0x1601 while the Spectranet is paged in
    ld a, 2
    rst CALLBAS   ; this is RST 0x10
    defw 0x1601

This allows you to keep the Spectranet paged in (via the CALL 0x3FF9 mechanism to page in, and 0x007C to unpage) until you decide to return to BASIC, which will add a bit of efficiency to your program by avoiding the need to run the page in/page out dispatcher.

Code examples

There are none just yet (apart from browsing SVN) - however, some assembly language code examples as well as C language examples should appear here in the near future.