[img] Detecting Interface 1 Presence
 MDFS::Info.Comp.Spectrum.ProgTips.Interface1.detect/htm Search  

It's useful to be able to detect if the Interface 1 is present. Then a program can chose to use Interface 1 facilities, such as saving to microdrive, and avoid using them if absent.

BASIC

One method is to check if the Interface 1 system variables are present. Before any Interface 1 command is used, no extended system variables are present. What is needed is a way to ensure the system variables are present, but which is usable on an unexpanded Spectrum.

The original Spectrum ROM has a bug in the CLOSE# routine that causes a crash with an attempt to close an unopened stream higher than stream 3. Consequently, the Interface 1 hooks into the CLOSE routine to fix the bug and to close Interface 1 channels. So, using CLOSE# will create the extended system variables if the Interface 1 is present.

Stream 0 defaults to the "K"eyboard channel and is re-attached to it at various times during program execution. So doing CLOSE#0 is effectively a null operation, with the side effect that the Interface 1 is woken up.

Location 23734 is the first byte of the CHANS area on an unexpanded Spectrum, and will contain 244, the low byte of the PRINT_OUT routine at &09F4. With the Interface 1 woken up, location 23734 is FLAGS3. As the top four bits only ever have one set at a time it can never contain &F4. So, the following code will set int1 to non-zero if the Interface 1 is present, and sets it to zero on an unexpanded Spectrum.

LET int1=0:CLOSE#0:IF PEEK 23734<>244 THEN LET int1=1
The program can then do things like:
IF int1 THEN CLEAR#:OPEN #9,"b"

Machine Code

From machine code it is easier to wake up the Interface 1 as there is a hook code specifically for it. However, on an unexpanded Spectrum this results in the odd error W CI\*]\k. (The JGHROM traps this error and converts it to Unknown command.) In machine code, though, you can trap this error and continue. This can be done with the following code.
XOR A:LD (INT1OK),A     ; Assume unexpanded Spectrum
LD HL,(ERR_SP):PUSH HL  ; Save current ERR_SP
CALL CheckInt1          ; Call the check code, stacking a return to here
POP HL:LD (ERR_SP),HL   ; Restore ERR_SP
; program continues...

.CheckInt1
LD (ERR_SP),SP          ; Point ERR_SP to the return from here
RST &08:DEFB &31        ; Attempt to initialise Interface 1
LD A,&FF:LD (INT1OK),A  ; Set flag to non-zero to show Interface 1 present
RET
An alternative method is as follows:
XOR A:LD (INT1OK),A     ; Assume unexpanded Spectrum
LD HL,(ERR_SP):PUSH HL  ; Save current ERR_SP
LD HL,CheckRet:PUSH HL  ; Fake a return address to CheckRet
RST &08:DEFB &31        ; Attempt to initialise Interface 1
LD A,&FF:LD (INT1OK),A  ; Set flag to non-zero to show Interface 1 present
POP HL                  ; Lose CheckRet from stack
.CheckRet               ; An error returns to here
POP HL:LD (ERR_SP),HL   ; Restore ERR_SP
; program continues...

Best viewed with Any Browser Valid HTML 4.0! Authored by J.G.Harston
Last update: 18-Oct-2004