Date : Wed, 11 Oct 2006 01:35:07
From : jgh@... (Jonathan Graham Harston)
Subject: Tube Documentation
Errors and bugs in Tube documentation & code
Acorn Application Note 004 "Tube Application Note" details the Acorn Tube
interface and software protocols. In the process of converting the PDF
version to text and documenting existing Tube host and client code I have
noticed several documentary errors and code bugs.
OSBYTE &8E, page 22
~~~~~~~~~~~~~~~~~~~
When the client calls OSBYTE, after sending the parameters it should check
for OSBYTE &8E and jump to check for a single acknowledge byte indicating if
code is to be entered.
OSBYTE &82 is 'Select language', and the only return is either an error (eg
'This is not a language') or a background data transfer and a call to enter
code.
The 6502 clients correctly checks this, but the Z80 client does not. Doing
A%2:X%=nn:CALL &FFF4 will cause the client to hang waiting for a full
three-byte OSBYTE acknowledge when only a single data execution acknowldge
is sent. Doing '*FX142,nn' works correctly as that is processed as an OSCLI
call, and OSCLI waits for a code entry acknowldgement byte.
Page 22 should read:
IF osbyteno=&9D THEN RETURN from protocol (no reply)
IF osbyteno=&8E THEN [
Wait for data in R2DATA and read it
If this byte=&80 then code has been loaded as a result
of the OSBYTE, and it should be entered at the address
given by the last R4 protocol type 4 address.
]
OSWORD control block lengths, p23 (also p4,5)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
OSWORD 5 is listed as sending a two-byte control block. OSWORD 5 is 'read
byte in I/O memory', with XY%?0 to XY?3 containing the address to be read.
Sending the bottom two bytes is sufficient for reading a location with 64K
of memory, but really the whole 4-byte address should be sent. The 65C102
CoProcessor v1.10 client sends four bytes, and the Master is able to use the
top two bytes to distinguish between IO memory at &FFFFxxxx and screen
memory at &FFFExxxx. There is no reason why OSWORD 5 in the host doesn't
also allow access to sideways ROMs with &FFFrxxxx, so really a Tube client
should send a full four bytes.
Mention is made of Tube clients sending and receiving 16 bytes for OSWORD 14
and OSWORD 15. This causes problems if the host implements a Real Time Clock
as OSWORD 14 returns a 25-character string.
In fact, the both the 6502 TUBE v1.10 client and the 65C102 CoProcessor
v1.10 client:
sends 1 and receives 24 bytes for OSWORD 14 and
sends 32 and receives 0 bytes for OSWORD 15
This results in the full clock string being sent with OSWORD 14, but OSWORD
14 omits the terminating <cr>.
When OSWORD calls with A>&7F are sent, the sent and received control block
lengths are in the first two bytes of the control block. The application
note omits to mention that lengths of &81 to &FF should send and receive
*no* data, they should be treated the same as a length of zero. "Up to 128
bytes" is mentioned on page 5 and page 10, but it should be specified in the
protocol description on page 23.
All hosts treat &81 to &FF as being zero. The 6502 clients correctly send
and wait for no data for &81 to &FF, but the Z80 v1.20 client does not. It
sends up to 255 bytes and waits for up to 255 bytes, causing the Tube
syncronisation to go off.
OSFILE object type, p24
~~~~~~~~~~~~~~~~~~~~~~~
The protocol states that R2DATA AND &7F is the object type. But why AND &7F?
This loses bit 7. The 6502 clients do not do this, returning the full 8-bit
object type, but the Z80 client does. Object type &FF is defined in other
Acorn documents as being a Run-Only file, but doing AND &7F means that
object type &FF is returned as &7F.
Execute in Client Processor, p26
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
While page 9 states that Transfer Type 4 is "execute in client processor",
it actually only passes an address to the client; the client picks up that
address in the foreground when the foreground action has completed, eg
OSCLI, OSBYTE &8E or STARTUP. It does not cause the client to call code from
the interupt code. Econet documentation implies that a RemoteJSR can cause a
jump to an address in the client processor, but there is nothing in the
client code to let this happen. A jump to code can only happen when OSCLI,
OSBYTE &8E or STARTUP waits for an acknowledgement byte.
Data transfer Sync Bytes, p26, 27
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The documentation for Transfer Types 4, 6 and 7 shouldn't really say "Wait
for data in R4DATA, discard it", it should say "Wait for and remove
synchronising byte from R4DATA", as with transfers 1 to 3.
STARTUP procedure
~~~~~~~~~~~~~~~~~
In the startup sequence the client should do something sensible if the
startup acknowledge is not &80, ie, there is no execute address to enter.
This will usually be because a Soft Break occured, and the host has not
recopied over the current language. The client should enter the current
program at its entry address. If this is not possible, the convention is to
set up an error handler and enter a '*' command prompt loop.
The Z80 client deviates from this in that after the startup sequence it
ignores the acknowledge and only enters the R4 transfer address if the
startup was a Hard Break or PowerOn Reset, otherwise it enters a '*' command
prompt loop. The Z80 BASIC ROM has to pretend a Soft Break is a Hard Break
to trick the host into sending over the current language and to trick the
client into expecting it.
If a program entered on the client relocates itself, it should restart
itself via the program entry mechanism so the client's current program
address is updated. Otherwise, on a Soft Break the client will try to
reenter the program at the address it was copied over to. This is usually
done by constructing a *GO command and passing it to OSCLI, as VIEW does.
This implies that the client should implement a '*GO' command to set a new
current program address and enter it.
Transfer Acknowldgements
~~~~~~~~~~~~~~~~~~~~~~~~
While the documentation, and the host code, transfers &7F/&80 acknowledges,
the client code should just check bit 7 rather than an exact match for
&7F/&80.
Bugs in Client Code
~~~~~~~~~~~~~~~~~~~
The Z80 v1.20 client code is fairly untidy, has quite a bit of duplicated
code, and has some bugs in it. There are only two that cause problems.
A data transfer can overwrite the stack. This can occur if the stack is at
the top of user memory at &F000 and a language is copied over to the top of
memory at &B000-&CFFF. The stack will be trampled on before the code can be
entered. OSCLI and OSBYTE should use a tempory stack to prevent this.
When entering code, the stack is left unbalanced if the code does not have a
ROM header. The naughty code is:
LD A,(HL)
CP ASC")"
JR NZ,LF826 ; No ROM header, enter as raw code
POP HL ; Get address of copyright message
DEC HL ; Point to rom type
; some other processing
.LF826
LD HL,(PROG) ; Fetch program address
JP (HL) ; And enter it
If there is no ROM header the address that was examined for a copyright
message is left on the stack, and the code cannot be RETurned from. A
solution is for transient code to have a ROM header, but this would make the
transient code the current program which may not be what is wanted. A better
solution is for transient Z80 code start with a POP to balance the stack.
The Z80 BASIC ROM fixes this problem by balancing the stack before entering.
In order that transient code can tell whether it has been called by the
unbalanced code or the balanced code with the BASIC ROM the flags are set
differently. If the stack is unbalanced, NZ is set If the stack is balanced,
Z is set, so transient code can start with:
JR Z,P%+3:POP AF
The Z80 client EnterCode routine always sets the current program address to
the entered address. It really should only do this for code with a ROM
header. This is ineffectual though as STARTUP ignores the startup
acknowledgement which would tell the client to re-enter the current program.
Suggestion for reading memory limits with OSBYTE
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
OSBYTE &83 and &84 read the bottom and the top of available memory. OSBYTE
&82 reads the high order word of the memory map. On the 6502 client, for
example, OSBYTE &82 returns &0000 and OSBYTE &83 returns &0800, showing that
the bottom of free memory is at &00000800.
A client with a memory map larger than 64K could allow programs to call
OSBYTEs &82,&83,&84 in sequence to find the 32-bit memory size. A suggestion
is for programs to do:
membot=OSBYTE(&83) :REM Read membot b0-b15
membot=membot OR OSBYTE(&82)<<16 :REM Read membot b16-b31
memtop=OSBYTE(&84) :REM Read memtop b0-b15
memtop=memtop OR OSBYTE(&82)<<16 :REM Read memtop b16-b31
To implement this, the client code should be set up so that OSBYTE &82
returns the top 16 bits of the lower memory limit. When OSBYTE &84 is called
it should flag internally that the next OSBYTE &82 should return the top 16
bits of the upper memory limit. Any OSBYTE call other than &83 should reset
the OSBYTE &82 behaviour to return the lower memory limit.
This is compatible with 16-bit memory maps as OSBYTE &82 will return the
same value in all cases, and is compatible with current 32-bit clients as
OSBYTE &82 returns &0000, indicating that the bottom of memory is in the
bottom 64K.
This could be implemented in ARM with something like:
.ByteHi
CMP R0,#&83
STRMNE (sp)!,R1
LDRBNE R1,[membot+2]
STRBNE R1,[topword+0]
LDRBNE R1,[membot+3]
STRBNE R1,[topword+1]
LDRMNE (sp)!,R1
BEQ Byte83
CMP R0,#&84
BEQ Byte84
CMP R0,#&82
BEQ Byte82
... process OBYTE call
.Byte82
LDRB R1,[topword+0]
LDRB R2,[topword+1]
MOV link,pc
.Byte83
LDRB R1,[membot+0]
LDRB R2,[membot+1]
MOV link,pc
.Byte84
LDRB R1,[memtop+2]
LDRB R2,[memtop+3]
STRB R1,[topword+0]
STRB R2,[topword+1]
LDRB R1,[memtop+0]
LDRB R2,[memtop+1]
MOV pc,link
.membot
EQUD memorymin
.memtop
EQUD memorymax
.topword
EQUW memorymin << 16
--
J.G.Harston - jgh@... - mdfs.net/User/JGH
BBC IDE Hard Drive Interface - http://mdfs.net/Info/Comp/BBC/IDE