
**********************************************************************
files: Plus128.TXT, Plus128.TAP
      [ModeSwap.BAS, ModeSwap.ASM] in
      http://www.users.globalnet.co.uk/~jg27paw4/pourri/modeswap.zip
       Token128.BAS, WhoGoesThere.ASM, [RAMtest2.ASM] in
       http://www.users.globalnet.co.uk/~jg27paw4/pourri/ramtest2.tap
**********************************************************************


** Switching 48k/128k modes within a BASIC program
   ===============================================
ModeSwap
ModeSwap includes a bonus error trap routine which can be used on any Spectrum. The method I use to determine the model is from an 8-year old c.s.s. posting "How to tell the difference?" by Geoff Wearmouth. Thanks also to GW et al. for the various ROM disassemblies which were invaluable in getting it to work.

There's a little BASIC demo and an assembler listing. The switch routine relies on the error trap routine to catch the "0 OK" report after the switch, which would otherwise quit the program, and redirect it to the BASIC error handler at line 9000 instead. As with Toggler, the program must always be started in 128k mode, as starting in 48k mode locks out the paging and thus the switching.

By the way, the reason the SPECTRUM command on the +2A/+3/+3e models gives that odd "OUT 7I 0:1" report is that it's still using $0013 as an address from which to stack a $FF byte for the error code, but on the +2A/+3/+3e it doesn't hold $FF any longer. I used $0014 instead.

ModeSwap.BAS [code]
   2 DEF FN m(v)=v-(-1+(256 AND v=255)): DEF FN p(a)=PEEK a+256*PEEK (a+1):
     DEF FN g(v)=v-FN h(v)*256: DEF FN h(v)=INT (v/256)
1000 CLEAR 32767: LOAD "mc"CODE: GO SUB 9115: GO SUB 2000: GO SUB 9120: STOP
2000 LET flags=PEEK 23611: LET bit4=INT (flags/16)-INT (flags/32)*2:
     LET mode=48+bit4*80: PRINT AT 0,0;CHR$ 163,"in ";mode;" mode",
2010 INPUT "Switch/Quit ";m$: IF m$="s" THEN RANDOMIZE USR 32834
2013 IF m$<>"q" THEN GO TO 2000
2099 RETURN
8999 REM Error Return
9000 PRINT "Report code ";FN m(PEEK 23681);" at line ";FN p(23615),:
     GO TO 2000
9114 REM Set Up Error Trap
9115 LET trap=32768: LET sp=FN p(23613):
     POKE sp,FN g(trap): POKE sp+1,FN h(trap):
     RETURN
9119 REM Reset Standard Error Trap to Match Mode/Model
9120 RESTORE 9129: LET ret=0: LET l=FN m(PEEK 19)+PEEK 5440:
     FOR a=1 TO 6: READ o,e: LET ret=ret+(e AND mode+l=o): NEXT a
9121 LET sp=FN p(23613): POKE sp,FN g(ret): POKE sp+1,FN h(ret):
     RETURN
9128 REM Standard Error Trap Addresses by Mode/Model
9129 DATA 48+83,4867,48+65,4867,48+233,4867,
     128+83,23325,128+65,23325,128+233,23354
[/code]

ModeSwap.ASM [code]
        org  32768

MCTRAP: dec  sp
        dec  sp
        bit  4,(iy+$01)
        jr   z,MODE48
        ld   a,(19)
        inc  a               ;A =168 if +2A/+3 else 0
        ld   l,a
        ld   a,(5440)        ;A =83 if Sinclair else 65 if Amstrad
        add  a,l             ;A =83 +128k; =65 +2; =233 +2A/+3/+3e
        cp   233
        jr   z,PLUS3M
MODE48: ld   hl,$1b9e        ;LINE-NEW
        push hl
        jr   MCCONT
PLUS3M: ld   hl,$1031        ;STMT-RET (+2A/+3/+3e ROM.1)
        push hl
        ld   hl,$5b2f        ;REGNUOY  (+2A/+3/+3e paging)
        push hl
MCCONT: LD   HL,(23621)      ;save PPC current line
        LD   (23615),HL      ;in LIST_SP
        LD   A,(23610)       ;save ERR_NR error code
        LD   (23681),A       ;in "not used"
        LD   A,$ff
        LD   (23610),A       ;reset error code
        LD   HL,9000         ;line number of BASIC error handler
        ld   a,1             ;statement number
        ld   d,a
MCLOOP: CALL $1f54           ;BREAK-KEY
        JP   C,$1e73         ;GO-TO-2 - jump if not BREAK
        JR   MCLOOP          ;ignore BREAK

MODESW: ld   a,(75)
        cp   191
        ret  z               ;16k/48k
        bit  4,(iy+$01)
        jr   nz,TO48
TO128:  set  4,(iy+$01)      ;signal "128K BASIC" mode
        jr   TO_ALL
TO48:   res  4,(iy+$01)      ;signal "48K BASIC" mode
        ld   hl,($5C4F)      ;(CHANS)
        ld   de,$0005
        add  hl,de           ;HL=channel info for "S"
        ld   de,$000a
        ex   de,hl
        add  hl,de
        ex   de,hl           ;DE=channel info for "P"
        ld   bc,$0004
        ldir                 ;replace channel info "P" with "S"
TO_ALL: ld   sp,($5c3d)      ;ERR_SP
        ld   hl,$0014        ;address of $FF in BASIC ROM, for report "0 OK"
        push hl
        ld   hl,$0008        ;address of ERROR_1
        push hl
        ret
[/code]


**********************************************************************



** A 128k Routine to Put Tokens in Strings
   =======================================
Token128
I was writing a DEF FN on the +128k and decided that I'd had enough of not being able to put function tokens in strings without a great palaver and so I wrote this little BASIC routine to do it for me. Just define the functions to be inserted as lower case strings in the DATA line with the correct CHR$ code following and in the main program insert the initial letter of the function as a UDG wherever it is to occur within a quoted string. So for FN insert UDG F, for AND use UDG A, etc. If two different functions with the same initial letter are required, then just give them different initial letters in the DATA statement; eg. for LEN & LN it could have ..."len",177,"uln",184...

Token128.BAS [code]
9990 DATA "fn",168,"and",198,"len",177,"or",197,"not",195,"",0
9991 INPUT "Line ";l: RESTORE l: LET a=FN p(23639):
     LET la=256*PEEK (a+1)+PEEK (a+2): LET ll=FN p(a+3): RESTORE 9990:
     LET ae=a+4+ll: LET n$="    "( TO 4-LEN STR$ l)+STR$ l+" ":
     IF l<>la THEN PRINT "No Line ";l: GO TO 9991
9992 LET b=a+4: READ t$,t: IF NOT t THEN GO TO 9991
9993 LET u=CODE t$+47: CLS :
     PRINT "Line:";l;" replace:";CHR$ u;" with:";CHR$ t: PRINT n$;#0;n$;
9995 LET b=b+1: LET p=PEEK b: IF p=14 THEN LET b=b+5: GO TO 9995
9997 PRINT #0;CHR$ p;: LET p=p-(p-t AND p=u): POKE b,p:
     PRINT CHR$ p;: GO TO 9995-(3 AND b>=ae):
     DEF FN p(a)=PEEK a+256*PEEK (a+1)
[/code]


**********************************************************************


** Which 128k Spectrum Is This?
   ============================
WhoGoesThere?
Here's what I use to distinguish the various 128k models, which I got from an old c.s.s. posting by Geoff Wearmouth (the bit in the middle). The lines referencing HL are part of my code to adjust the stack to redirect the return from a larger routine. The checking bit can be easily converted to BASIC with a few PEEKs, etc. For "bit  4,(iy+$01)" use:

LET flags=PEEK 23611: LET bit4=INT (flags/16)-INT (flags/32)*2

WhoGoesThere.ASM [code]
MODE48: ld   hl,$1b9e        ;LINE-NEW
        bit  4,(iy+$01)
        jr   z,MCCONT        ;48k
        ld   a,($13)         ;128k;[color=green]
        inc  a               ;A =168 if +2A/+3/+3e else 0;[/color]
        ld   d,a
        ld   a,($1540)       ;A =83 if Sinclair else 65 if Amstrad;[color=green]
        add  a,d             ;A =83 +128k; =65 +2; =233 +2A/+3/+3e;[/color]
        cp   $e9
        jr   nz,MCCONT
PLUS3M: ld   hl,$1031        ;STMT-RET (+2A/+3/+3e ROM.1)
        push hl
        ld   hl,$5b2f        ;REGNUOY  (+2A/+3/+3e paging)
MCCONT: push hl
[/code]


**********************************************************************


** How to check the whole memory on a +2 grey
   ==========================================
RAMtest2
You could try this [url=http://www.users.globalnet.co.uk/~jg27paw4/pourri/ramtest2.asm]RAMtest2[/url] routine. It's based on the memory check section of the self-test code from the +3 that runs from the test screen after pressing "QAZPLM", with the bits for ROMs 2&3 and the "special" paging configurations removed; so it does the RAM I/O, paging & checksums (which are ROM0=145 & ROM1=112).

I draw particular attention to these comments at the start of the listing.

[code]
        ; Spectrum +2 (not +2A!) RAM test
        ; assemble & run at $7000 (28672) then $8000 (32768)
        ; NOT RELOCATABLE!
[/code]

As it's running in RAM it can't check the page in which it is running, so it has to be assembled & run twice, once in page 5 & once in page 2. It does a "DI : HALT" at the end, so the computer has to be reset afterwards.

Possible results are:

[list=a]
[*]"RAM pass, excluding page [2 or 5]"
[*]"RAM fail: address xxxx, page x" (RAM I/O error)
[*]"RAM fail: address 0000, page x" (ULA paging error)
[*]"ROM fail: address 0000, page [0 or 1]" (ROM checksum error)
[*]It crashes, indicating a probable fault in RAM page 5 (or 2).
[/list]

   ==========================================

Assemble the code at 28672 and then:
	SSAVE "RAMtest2-5" CODE 28672,532
Assemble the code at 32768 and then:
	SSAVE "RAMtest2-2" CODE 28672,532

Reset the computer and then:
LOAD "RAMtest2-5": RANDOMIZE USR 28672
Reset the computer and then:
LOAD "RAMtest2-2": RANDOMIZE USR 32768

If you have a potential RAM error then it would be a good idea to check that the files have loaded correctly, so after loading check the code with:
LET t=0: FOR a=????? TO ?????+531: LET t=t+PEEK a: NEXT a: PRINT t
where ????? is either 28672 or 32768, depending upon which file's been loaded. The respective sums should be 57017 and 57545.

   ==========================================

The two routines combined will check all the pages, as the page not checked by one version will be checked by the other version. They include this ULA check from the +3 self-test program.

[code]
ULACHEK:ld   a,$00           ; start with page 0
        ld   hl,$cafe        ; location to use
        ld   bc,$7ffd
ULALD:  out  (c),a           ; page in page
        ld   (hl),a          ; and store page number
        inc  a
        cp   $08
        jr   nz,ULALD        ; back for more
ULACP:  dec  a
        out  (c),a           ; page in page
        cp   (hl)            ; check correct number
        jp   nz,NOTOK1       ; if not, go to signal ULA error
        and  a
        jr   nz,ULACP        ; back for more
[/code]

It's putting the page number in $cafe, swapping each page out and back in again, and checking that the number still matches the page. Jumping to NOTOK1 would give ""RAM fail: address 0000, page x". Maybe it would be worth running the tests both immediately after switch-on and then later after the computer had been turned on for some time; ie. "cold" and "hot"?


**********************************************************************


** Problem with PRINT #stream on +128k
   ===================================

Working on a +128k, I want to choose whether all report output goes to screen or printer, so I use PRINT #stream where stream = 2 or 3 for screen or printer. If I type in a simple statement like:
	LET stream=3: PRINT #stream;"stuff"
either as a direct command, or with a line number and RUN it, then it prints to the printer as if it was LPRINT. However, if the same form of command is encountered in the depths of the program on which I'm currently working, it gives an "8 End of file" error if stream=3 (it works OK if stream=2).

If I run the program, load the data files, quit, then repeatedly call the print subroutine with GOSUB 1719, it will fail (when stream=3) with "8 End of file" on calls 1,3,5,7 etc. and succeed on calls 2,4,6,8 etc. This is the print subroutine:

[code]
1719 PRINT #stream; INVERSE 1;t$(1);TAB 0;
1720 FOR i=ea TO ez:
       LET start=FN s(i$(i)): LET length=FN l(i$(i)):
       PRINT #stream;FN p$(i);: LET col=0
1730   FOR e=start TO start+length-2 STEP 2:
         LET col=(col AND col<28)+4: PRINT #stream;TAB col;FN p(e);:
       NEXT e:
       PRINT #stream
1790 NEXT i: RETURN
[/code]

   ===================================

http://users.comlab.ox.ac.uk/ian.collier/Spectrum/+3_oddities.txt

   ===================================

I managed to sort it out from BASIC after all. It occurred to me that, as the ROM bug is with the handling of colour control codes to stream #3, then the INVERSE 1 in the first PRINT #stream statement could be the reason for it going wrong, so I took it out - and it worked.

So the lesson is not to use attribute control codes with LPRINT / PRINT #3 on the 128k models, as they cause an "8 End of file" error. As the only useful one for the printer is INVERSE then it's no great loss.


**********************************************************************


** BEEP durations and PLAY equivalents
   ===================================

I thought that this would be easy ... So I found page 120 in the +2 manual, which lists the 12 notes available. "Twelve?! A mere trifle! All I need to do is find out how long each of them lasts." Hmmm - it doesn't appear to tell me in the manual. So ...

Then, I found this very nice page: [url=http:http://cnx.org/content/m10945/latest/]Duration: Note Lengths in Written Music[/url]. So I read through that - but I didn't seem to be getting anywhere nearer to finding the answer. Then, near the bottom:

[quote]So how long does each of these notes actually last? That depends on a couple of things. [D'oh!] A written note lasts for a certain amount of time measured in beats. [Aha!] To find out exactly how many beats it takes, you must know the [url=http://cnx.org/content/m10956/latest/]Time Signature[/url]. [Right!] And to find out how long a beat is, [Yes?] you need to know the [url=http://cnx.org/content/m11648/latest/]Tempo[/url]. [D'oh!] [/quote]

So I had a read of those two pages ... then my brain exploded.


**********************************************************************


** Spectrum 128k Programming Tutorials
   ===================================

There's the Melbourne House book "Spectrum+2 Machine Language for the Absolute Beginner", and the Amstrad manual for the +2 has some useful info. There's also Toni Baker's 128k articles from ZX Computing:
[url=http://www.users.globalnet.co.uk/~jg27paw4/type-ins/zx-comp/zx8612.zip]128 Animation[/url]
[url=http://www.users.globalnet.co.uk/~jg27paw4/type-ins/zx-comp/zx8606.zip]Keypad Interrupt Routine[/url]
[url=http://www.users.globalnet.co.uk/~jg27paw4/type-ins/zx-comp/into_128.zip]Into the 128[/url]
[url=http://www.users.globalnet.co.uk/~jg27paw4/type-ins/zx-comp/streams.zip]Streams and Channels[/url]


**********************************************************************


** Editing Shortcut Keys for the Spectrum 128k
   ===========================================

Here's a handy-dandy diagram showing all of the 128k editor shortcuts, as provided by Toni Baker with a letter in the Nov'86 issue of ZX Computing. The colour scheme is of my own devising. I re-drew the whole thing, actually, as it was a bit small in the mag and the different modes were only in shades of grey so they weren't very distinct from each other.

[center][img]http://www.users.globalnet.co.uk/~jg27paw4/pourri/keypad.gif[/img][/center]


**********************************************************************