10
20
30
40
50
60
70
80 ON ERROR REPORT:PRINT ERL:END
90 :
100 ioport%=&00FD
110 :
120 DIM mcode% &8000:ver$="1.05"
130 sp=13:link=14:pc=15
140 FOR p=0 TO 1
150 P%=0:O%=mcode%
160 [OPT p*3+4
170 EQUD 0:\start
180 EQUD Initialise
190 EQUD Finalise
200 EQUD Service
210 EQUD TitleStr
220 EQUD HelpStr
230 EQUD CommandTable
240 EQUD 0:\ SWI chunk number
250 EQUD 0:\ SWI chunk handler
260 EQUD 0:\ SWI decoding table
270 EQUD 0:\ SWI decoding code
280 :
290 .TitleStr
300 EQUS "ROMBox"
310 EQUB 0
320 ALIGN
330 .HelpStr
340 EQUS "Spectrum ROMBox"+CHR$9+ver$+" ("+MID$(TIME$,5,11)+") © 2002 J.G.Harston"
350 EQUB 0
360 ALIGN
370 :
380 .CommandTable
390 EQUS "ROMBox":EQUB 0:ALIGN:EQUD ROMBoxInit:EQUD &00010001:EQUD ROMBoxInit_Syntax:EQUD ROMBoxInit_Help
400 EQUS "ROMBoxLoad":EQUB 0:ALIGN:EQUD ROMBoxLoad:EQUD &00020002:EQUD ROMBoxLoad_Syntax:EQUD ROMBoxLoad_Help
410 EQUS "ROMBoxList":EQUB 0:ALIGN:EQUD ROMBoxList:EQUD &00000000:EQUD ROMBoxList_Syntax:EQUD ROMBoxList_Help
420 EQUS "ROMBoxPort":EQUB 0:ALIGN:EQUD ROMBoxPort:EQUD &00000000:EQUD ROMBoxPort_Syntax:EQUD ROMBoxPort_Help
430 \ ROMBoxRAM
440 \ ROMBoxROM
450 \ ?
460 EQUB 0:ALIGN
470 :
480 .ROMBoxInit_Help
490 EQUS "Registers a module or application with ROMBox":EQUB 13
500 .ROMBoxInit_Syntax
510 EQUS "Syntax: ROMBox <module name>|<address>"
520 EQUB 0:ALIGN
530 :
540 .ROMBoxLoad_Help
550 EQUS "Loads a ROM image into a ROMBox slot":EQUB 13
560 .ROMBoxLoad_Syntax
570 EQUS "Syntax: ROMBoxLoad <ROM number> <filename>"
580 EQUB 0:ALIGN
590 :
600 .ROMBoxList_Help
610 EQUS "Lists loaded ROMBox ROMs":EQUB 13
620 .ROMBoxList_Syntax
630 EQUS "Syntax: ROMBoxList"
640 EQUB 0:ALIGN
650 :
660 .ROMBoxPort_Help
670 EQUS "Sets or displays ROMBox I/O port":EQUB 13
680 .ROMBoxPort_Syntax
690 EQUS "Syntax: ROMBoxPort (<port>)"
700 EQUB 0:ALIGN
710 :
720 :
730 .Initialise
740 stmfd (sp)!,{link}
750 ldr r0,[r12]
760 orrs r0,r0,#0
770 bne Reinitialise ; We already have workspace
780 mov r0,#6
790 mov r3,#10*4 ; We want 10 words
800 swi "XOS_Module"
810 ldmvsfd (sp)!,{pc} ; Memory claim failed
820 str r2,[r12] ; Store in w/s pointer
830 mov r0,#0 ; Clear ROM pointers
840 mov r1,#0
850 .InitClear
860 str r0,[r2,r1]
870 add r1,r1,#4
880 cmp r1,#10*4 ; 8 ROM pointers, module pointer, OUT copy
890 bcc InitClear
900 .Reinitialise
910 ldmfd (sp)!,{pc}
920 :
930 :
940 .Finalise
950 stmfd (sp)!,{link}
960 ldr r3,[r12] ; Get pointer to module's workspace
970 orrs r3,r3,#0
980 beq Finalised ; No workspace (shouldn't ever get called this way)
990 mov r4,#0 ; Loop from workspace+0
1000 .FinalLoop
1010 ldr r2,[r3,r4] ; Get pointer to ROM space
1020 orrs r2,r2,#0 ; Is is zero?
1030 add r4,r4,#4 ; Point to next pointer
1040 movne r0,#7
1050 swine "XOS_Module" ; Release ROM space if it exists
1060 cmp r4,#4*8
1070 bcc FinalLoop ; 8 ROM pointers
1080 ldr r2,[r12] ; Pointer to module's workspace
1090 mov r0,#7
1100 swi "XOS_Module" ; Release it
1110 .Finalised
1120 ldmfd (sp)!,{pc}
1130 :
1140 :
1150 .ROMBoxInit ; *ROMBox <module name>|<address>
1160 mov pc,link
1170 :
1180 :
1190 .ROMBoxLoad ; *ROMBoxLoad <ROM number> <filename>
1200 stmfd (sp)!,{link}
1210 bl ROMNumber ; r6=ROM number, r0=>filename
1220 mov r7,r0 ; r7=>filename
1230 mov r1,r7
1240 mov r0,#5
1250 swi "OS_File" ; Read info on file
1260 cmp r0,#1 ; File present?
1270 bne ROMNotFound
1280 cmp r4,#&4000 ; Check file's length
1290 beq ROMLoadOk
1300 bcs ROMFileTooLong ; Can only load up to 16k
1310 .ROMLoadOk
1320 ldr r12,[r12] ; Get address of workspace
1330 ldr r2,[r12,r6,lsl #2] ; Get ROM's address
1340 orrs r2,r2,#0
1350 moveq r0,#6 ; If zero, claim 16k of RMA
1360 moveq r3,#&4000
1370 addeq r3,r3,#12 ; Claims &400C bytes to quad-align
1380 swieq "OS_Module"
1390 streq r2,[r12,r6,lsl #2]
1400 ; r7=>filename
1410 ; r3=index into ROM memory
1420 ; r2=>ROM memory-12
1430 ; r1=index into filename
1440 ; r0=byte
1450 mov r3,#0 ; index into ROM memory
1460 mov r1,#0 ; index into filename
1470 .ROMLoadScan ; Look for end of filename
1480 ldrb r0,[r7,r1]
1490 add r1,r1,#1
1500 cmp r0,#ASC"!"
1510 bcs ROMLoadScan
1520 .ROMLoadScan2
1530 subs r1,r1,#1
1540 beq ROMLoadName ; Moved back to start of filename
1550 ldrb r0,[r7,r1]
1560 cmp r0,#ASC"."
1570 cmpne r0,#ASC":" ; Loop back until dot or colon
1580 bne ROMLoadScan2
1590 add r1,r1,#1
1600 .ROMLoadName
1610 ldrb r0,[r7,r1]
1620 strb r0,[r2,r3]
1630 add r1,r1,#1
1640 add r3,r3,#1
1650 cmp r3,#11
1660 bcs ROMLoadLeaf
1670 cmp r0,#ASC"!"
1680 bcs ROMLoadName
1690 sub r3,r3,#1
1700 .ROMLoadLeaf
1710 mov r0,#0
1720 strb r0,[r2,r3] ; zero terminator
1730 ;
1740 add r2,r2,#12 ; Point to ROM image start
1750 mov r0,#&FF ; 'LOAD'
1760 mov r1,r7
1770 mov r3,#0 ; Load to address in r2
1780 swi "OS_File"
1790 sub r2,r2,#12 ; Point to before ROM memory
1800 ldmfd (sp)!,{pc}
1810 :
1820 .ROMFileTooLong
1830 ADR r0,ROMFileTooLongErr
1840 SWI "OS_GenerateError"
1850 .ROMFileTooLongErr
1860 EQUD 255
1870 EQUS "ROM too long"
1880 EQUB 0:ALIGN
1890 :
1900 .ROMNotFound
1910 ADR r0,ROMNotFoundErr
1920 SWI "OS_GenerateError"
1930 .ROMNotFoundErr
1940 EQUD 214
1950 EQUS "ROM not found"
1960 EQUB 0:ALIGN
1970 :
1980 :
1990 .ROMBoxSave ; *ROMBoxLoad <ROM number> <filename>
2000 mov pc,link
2010 :
2020 :
2030 .ROMNumber
2040 ldrb r6,[r0,#1] ; Look at character after current
2050 cmp r6,#ASC" "
2060 addne r0,r0,#1
2070 bne ROMNumber ; Skip until last digit
2080 ldrb r6,[r0],#1 ; Get last digit and inc. pointer
2090 and r6,r6,#7 ; Reduce to 0..7
2100 .ROMNumberSpc
2110 ldrb r1,[r0]
2120 cmp r1,#ASC" "
2130 addeq r0,r0,#1
2140 beq ROMNumberSpc ; Skip spaces after digit
2150 mov pc,link ; r6=ROM number 0..7
2160 :
2170 :
2180 .ROMBoxList ; *ROMBoxList
2190 stmfd (sp)!,{link}
2200 ldr r12,[r12] ; Get workspace pointer
2210 mov r3,#0 ; Start with ROM 0
2220 .ROMListLoop
2230 ldr r2,[r12,r3,lsl #2]
2240 orrs r2,r2,#0 ; Get address of ROM image
2250 beq ROMListEmpty
2260 orr r0,r3,#ASC"0"
2270 swi "OS_WriteC"
2280 swi 256+ASC":"
2290 swi 256+ASC" " ; Output ROM number
2300 mov r1,#11 ; Max of 11 characters
2310 .ROMListName
2320 ldrb r0,[r2],#1 ; Get character
2330 cmp r0,#ASC"!"
2340 bcc ROMListPad ; End of leafname
2350 swi "OS_WriteC"
2360 subs r1,r1,#1
2370 bne ROMListName
2380 .ROMListPad
2390 swi 256+32 ; Pad with spaces
2400 add r2,r2,#1
2410 subs r1,r1,#1
2420 bpl ROMListPad
2430 add r2,r2,#256 ; Point to near begining of ROM image
2440 .ROMListLook
2450 add r2,r2,#1
2460 ldrb r0,[r2,#3] ; Look for four ASCII values
2470 cmp r0,#ASC"!":bcc ROMListLook
2480 cmp r0,#ASC"z":bcs ROMListLook
2490 ldrb r0,[r2,#2]
2500 cmp r0,#ASC"!":bcc ROMListLook
2510 cmp r0,#ASC"z":bcs ROMListLook
2520 ldrb r0,[r2,#1]
2530 cmp r0,#ASC"!":bcc ROMListLook
2540 cmp r0,#ASC"z":bcs ROMListLook
2550 ldrb r0,[r2,#0]
2560 cmp r0,#ASC"!":bcc ROMListLook
2570 cmp r0,#ASC"z":bcs ROMListLook
2580 .ROMListTitle
2590 swi "OS_WriteC"
2600 add r2,r2,#1
2610 ldrb r0,[r2]
2620 cmp r0,#ASC" "
2630 bcc ROMListDone
2640 cmp r0,#ASC"~"+1
2650 bcc ROMListTitle
2660 .ROMListDone
2670 swi "OS_NewLine"
2680 .ROMListEmpty
2690 add r3,r3,#1
2700 cmp r3,#8
2710 bcc ROMListLoop
2720 ldmfd (sp)!,{pc}
2730 :
2740 :
2750 .ROMBoxPort ; *ROMBoxPort (<ioport>)
2760 mov pc,link
2770 :
2780 :
2790 .Service
2800 \ In r0=mem[0]
2810 \ r1=Srv_Z80Reset - &80AC2
2820 \ r1=Srv_Z80OUT - &80AC1
2830 \ r2=port
2840 \ r3=value
2850 \ r4=Module base
2860 \
2870 stmfd (sp)!,{r1} ; Check for Srv_Z80OUT or Z80Reset
2880 and r1,r1,#&FF000
2890 cmp r1,#&80000 ; &80xxx ?
2900 ldmfd (sp)!,{r1}
2910 movne pc,link ; No
2920 stmfd (sp)!,{r1}
2930 and r1,r1,#&FF0
2940 cmp r1,#&AC0 ; &80ACx ?
2950 ldmfd (sp)!,{r1}
2960 movne pc,link ; No
2970 ldr r12,[r12] ; Get workspace address
2980 stmfd (sp)!,{r1}
2990 and r1,r1,#&F
3000 cmp r1,#&1 ; &80AC1 ?
3010 beq Service_Z80OUT
3020 cmp r1,#&2 ; &80AC2 ?
3030 beq Service_Z80Reset
3040 ldmfd (sp)!,{r1}
3050 mov pc,link ; No
3060 :
3070 :
3080 .Service_Z80Reset
3090 stmfd (sp)!,{link}
3100 mov r3,#0
3110 bl ROMReset ; Select ROM 0 on Reset
3120 ldmfd (sp)!,{link}
3130 ldmfd (sp)!,{r1} ; Restore call number
3140 mov pc,link ; Don't claim
3150 :
3160 :
3170 .Service_Z80OUT
3180 ldmfd (sp)!,{r1}
3190 stmfd (sp)!,{r2} ; Now check for port &xxFD
3200 and r2,r2,#&FF
3210 cmp r2,#ioport%AND&FF ; port &xxFD ?
3220 ldmfd (sp)!,{r2}
3230 movne pc,link ; No
3240 ; OUT &xxFD,romnumber*16 (missing b5)
3250 ; r0=z80mem[0]
3260 ; r1=corruptable
3270 ; r2=corruptable
3280 ; r3=OUT value [ROM number] - must preserve!
3290 ; r4=corruptable
3300 ; stack balanced
3310 .ROMSelect
3320 ldrb r2,[r12,#9*4] ; Last OUT value
3330 cmp r3,r2 ; Is this OUT same as last?
3340 beq ROMSelected ; Already paged in
3350 .ROMReset
3360 strb r3,[r12,#9*4] ; Store flags word
3370 movs r2,r3,lsr #5 ; r2=OUT value DIV 32, Cy=OUT value b4
3380 and r2,r2,#6 ; ROM number = 0,2,4,6
3390 orrcs r2,r2,#1 ; ROM number = 0..7
3400 ;
3410 ;cmp r2,#8
3420 ;and r2,r2,#13
3430 ;orrcs r2,r2,#2 ; ROM number = 0..7
3440 ldr r2,[r12,r2,lsl #2]
3450 orrs r2,r2,#0 ; Get address of ROM memory
3460 moveq r1,#0 ; No ROM in this slot, so
3470 moveq pc,link ; claim and exit doing nothing
3480 ; Enter fast quad-word copy loop
3490 stmfd (sp)!,{r3-r10} ; Save everything for speed
3500 add r8,r2,#12 ; Point to quad-aligned ROM image start
3510 mov r9,r0 ; Point to z80mem[0]
3520 mov r10,#0 ; Copy address pointer
3530 ;
3540 ; r8=ROM address - src
3550 ; r9=z80mem[0] - dst
3560 ; r10=loop offset
3570 ;
3580 .ROMReadLoop
3590 ldmia r8!,{r0-r7} ; Fetch 8 quad-aligned words
3600 stmia r9!,{r0-r7} ; Store them (may not be quad-aligned)
3610 add r10,r10,#32
3620 cmp r10,#&4000 ; Loop for 16k
3630 bcc ROMReadLoop
3640 ldmfd (sp)!,{r3-r10} ; Get registers back
3650 :
3660 .ROMSelected
3670 mov r1,#0 ; Claim the call
3680 mov pc,link
3690 :
3700 ALIGN
3710 ]NEXT
3720 OSCLI"SAVE ROMBox "+STR$~mcode%+" "+STR$~O%
3730 *SetType ROMBox Module
3740 *Stamp ROMBox
3750 END