; This was my quick and dirty test code to test 128K file saving ; Which I've released as an example to show the new scatterloading tzx files ; ; I hope it's not actually confusing ; ; So look near the end... AppFilename equ "PagingTest" ; What we're called (for file generation) zeusemulate "128K" ; Set a model with 128K of RAM ; Print control codes cDel equ $08 ; cCret equ $0D ; cCls equ $0C ; cClr equ $1B ; cHome equ $0E ; ; Print colour select characters cBlack equ $00 ; cBlue equ $01 ; cRed equ $02 ; cMagenta equ $03 ; cGreen equ $04 ; cCyan equ $05 ; cYellow equ $06 ; cWhite equ $07 ; ; For this example we want to build code that will run at location $C000, but we're going to ; have Zeus do that for all 8 memory pages. ; ; So the first thing this code illustrates is how to use pages in Zeus. Basically Zeus can address ; more than 64K of memory, and Zeus uses this to map the pages to addresses higher than 64K. It is ; important to understand that this means some of the pages can be "seen" at two different places ; in Zeus - for example a 128K Spectrum maps page 5 at $4000, and so does Zeus. But Zeus *also* ; maps page 5 at $24000... so the same byte is visible at $4000 and at $24000. As far as Zeus is ; concerned it doesn't matter which you use. ; ; So, how do you use Zeus to write code for paged memory? ; ; Well, to put data in page 0 you could simply do this: ; ; org $10000 ; <- note that is NOT $1000, it's 65536d ; db "Hello, this text is being put in page 0" ; ; But if you are trying to build code that's going to run in a page it's not enough to just tell ; Zeus where to put it, you have to know what address it's going to be mapped at when it's run and ; tell Zeus that (so the addresses Zeus uses in the code are right) but also tell Zeus that Zeus ; should actually write the bytes to a different area of memory. This is known as "DISPLACING" the ; code and you do that with the DISP operator. DISP is simple, it just stores the displacement you ; put after DISP and Zeus adds that displacement to the addresses it uses when it stores code. ; ; So, for example, if you did this: ; ; org $4000 ; Build code to run at $4000 ; disp $1000 ; Put put it in memory at $5000 (which is $4000 + $1000) ; db "This string is put in memory starting at $5000",0 ; ; So, in practice let's say we want to page memory using the top 16K block (since the Spectrum memory ; paging is so limited that this is the best option) and this means that we want the code to be ; running at $C000..$FFFF, so we need to use "ORG $C000" to tell Zeus to build the code to run ; at addresses starting from $C000... but if we actually want it put in code page 0, which starts at ; $10000, we need to have a "DISP $4000" which will add $C000+$4000 to get $10000 ; ; In practice it's better not to have to work out what actual DISP value we need but to let Zeus do ; that calculation for us. So what we do is "ORG $C000" followed by "DISP [requiredpageaddress]-*" ; So for example "DISP $10000-*" for page 0, "DISP $14000-*" for page 1, and so on. ; ; Now, another issue with paging memory is that only one page is "visible" to the Z80 at any given ; time. So you have to be careful when writing code not to have any of it refer to things that are ; not visible at the time it's running... if you have page 0 mapped at $C000, you cannot see anything ; in any other page at $C000. ; ; One way Zeus can help you with this is to use the proc/endp structure to make all the labels ; in a page local to a single procedure. That way code in one page/procedure cannot simply ; refer to the labels in another page using their name, it has to use the procedure name prefix. ; Nor can code outside the page simply refer to routines in it without the prefix. But code in ; any given page can refer to local lables as normal... and code in any page can re-use the same ; names for different addresses without clashing. ; ; This example code shows all of these techniques: ; This is the code for RAM page 0 and we're going to put it inside a procedure with the name ; "CodePage_0", because that will remind us which page it's in whenever we use it. CodePage_0 proc ; CodePage_0 is the name of this procedure ; All the labels from this point on are local to procedure "CodePage_0" ; In modern parlance we'd probably call procedures "namespaces" ; We build code that will run at $C000, but put it at $10000 ZeusAddr equ $10000 ; Code page 0 ; Note - I could have obtained the value $10000 using "zeuspage(0)" org $C000 ; Build the code to run at $C000 disp ZeusAddr-* ; But displace it so it goes in memory at $10000 String db "This string is in page 0",0 ; See below for how to use this label StringEnd equ * pend ; End of procedure, back to global scope. ; This is the code for RAM page 1 CodePage_1 proc ; ZeusAddr equ $14000 ; Code page 1 ; Note - I could have obtained the value $14000 using "zeuspage(1)" and so on org $C000 ; Build the code to run at $C000 disp ZeusAddr-* ; But displace it so it goes in memory at $14000 ; Let's pretend we have some code here nop ; Some bytes nop ; Some bytes nop ; Some bytes ; Now! Pay attention. This string uses the same name as the string in Page 0, but it is NOT at the ; same address. That one started at $C000, this one starts at $C003 String db "This string is in page 1",0 ; StringEnd equ * pend ; ; This is the code for RAM page 2 CodePage_2 proc ; ZeusAddr equ $18000 ; Code page 2 org $C000 ; Build the code to run at $C000 disp ZeusAddr-* ; But displace it so it goes in memory at $18000 String db "This string is in page 2",0 ; StringEnd equ * pend ; ; This is the code for RAM page 3 CodePage_3 proc ; ZeusAddr equ $1C000 ; Code page 3 org $C000 ; Build the code to run at $C000 disp ZeusAddr-* ; But displace it so it goes in memory at $1C000 String db "This string is in page 3",0 ; StringEnd equ * pend ; ; This is the code for RAM page 4 CodePage_4 proc ; ZeusAddr equ $20000 ; Code page 4 org $C000 ; Build the code to run at $C000 disp ZeusAddr-* ; But displace it so it goes in memory at $20000 String db "This string is in page 4",0 ; StringEnd equ * pend ; ; This is the code for RAM page 5 CodePage_5 proc ; ZeusAddr equ $24000 ; Code page 5 org $C000 ; Build the code to run at $C000 disp ZeusAddr-* ; But displace it so it goes in memory at $24000 ds $2000 ; We need to avoid the screen memory in Page 5 String db "This string is in page 5",0 ; StringEnd equ * pend ; ; This is the code for RAM page 6 CodePage_6 proc ; ZeusAddr equ $28000 ; Code page 6 org $C000 ; Build the code to run at $C000 disp ZeusAddr-* ; But displace it so it goes in memory at $28000 String db "This string is in page 6",0 ; StringEnd equ * pend ; ; This is the code for RAM page 7 CodePage_7 proc ; ZeusAddr equ $2C000 ; Code page 7 org $C000 ; Build the code to run at $C000 disp ZeusAddr-* ; But displace it so it goes in memory at $2C000 String db "This string is in page 7",0 ; StringEnd equ * pend ; ; Remember to clear the displacement or we'll displace the rest of this disp 0 ; No more displacement ; We put the code out of the way at $9000 org $9000 ; Start of application AppEntry equ * ; Replace these lines with your code Start di ; Make sure we're not interrupted ld sp,$C000 ; Set the stack to the top of memory ld a,cCls ; Clear the screen call Print ; ld a,cBlue ; Show the first in blue call Print ; ; Page 0 ld bc,$7FFD ; The memory paging port ld a,#0 ; Start with page 0 out (c),a ; ; Now! Pay attention to how Zeus refers to local labels from outside the ; procedure containing them, using "procedurename.localname" ; So this next line gets the address of the String in page 0, which is $C000 ld hl,CodePage_0.String ; Point at the string in page 0 call PrintStr ; Show the value ld a,cCret ; CR call Print ; ; Page 1 ld bc,$7FFD ; The memory paging port ld a,#1 ; Page 1 out (c),a ; ; This next line gets the address of the String in page 1, which is $C003 ld hl,CodePage_1.String ; Point at the string in this page call PrintStr ; Show the value ld a,cCret ; CR call Print ; ; Page 2 ld bc,$7FFD ; The memory paging port ld a,#2 ; Page 2 out (c),a ; ld hl,CodePage_2.String ; Point at the string in this page call PrintStr ; Show the value ld a,cCret ; CR call Print ; ; Page 3 ld bc,$7FFD ; The memory paging port ld a,#3 ; Page 3 out (c),a ; ld hl,CodePage_3.String ; Point at the string in this page call PrintStr ; Show the value ld a,cCret ; CR call Print ; ; Page 4 ld bc,$7FFD ; The memory paging port ld a,#4 ; Page 4 out (c),a ; ld hl,CodePage_4.String ; Point at the string in this page call PrintStr ; Show the value ld a,cCret ; CR call Print ; ; Page 5 ld bc,$7FFD ; The memory paging port ld a,#5 ; Page 5 out (c),a ; ld hl,CodePage_5.String ; Point at the string in this page call PrintStr ; Show the value ld a,cCret ; CR call Print ; ; Page 6 ld bc,$7FFD ; The memory paging port ld a,#6 ; Page 6 out (c),a ; ld hl,CodePage_6.String ; Point at the string in this page call PrintStr ; Show the value ld a,cCret ; CR call Print ; ; Page 7 ld bc,$7FFD ; The memory paging port ld a,#7 ; Page 7 out (c),a ; ld hl,CodePage_7.String ; Point at the string in this page call PrintStr ; Show the value ld a,cCret ; CR call Print ; ; We're done... halt ; Stop all the excitement ; Print the zero-terminated string pointed to by HL PrintStr proc ; push hl,af ; Loop ld a,(hl) ; inc hl ; or a ; jr z,Exit ; call Print ; jr Loop ; Exit pop af,hl ; retp ; (RET) ; Print the string following the call in memory PrintStrFollow proc ; ex (sp),hl ; push af ; Loop ld a,(hl) ; inc hl ; or a ; jr z,Exit ; call Print ; jr Loop ; Exit pop af ; ex (sp),hl ; retp ; (RET) ; Print the contents of HL in hex PrintHex_HL ld a,h ; call PrintHex_A ; ld a,l ; ; Print the contents of A in hex PrintHex_A call PH2 ; PH2 rrca ; rrca ; rrca ; rrca ; push af ; and $0F ; add a,"0" ; cp "9"+1 ; jr c,PH3 ; add a,"A"-("9"+1) ; PH3 call Print ; pop af ; ret ; ; Print a space Print_Sp push af ; Save ld a,' ' ; Print a space call Print ; pop af ; Done ret ; ; A print character routine. Proportionally spaced characters Print push ix,hl,de,bc,af ; Save them cp cWhite+1 ; jr c,PrintColour ; cp cCls ; jr z,PrintCls ; cp cCret ; jr z,PrintCret ; cp cHome ; jr z,PrintHome ; ld h,PrintWidths/256 ; ld l,a ; ld a,(pCurX) ; add a,(hl) ; call c,PrintCret ; call PrintOutChar ; ld a,(pCurX) ; add a,(hl) ; ld (pCurX),a ; PrintExit pop af,bc,de,hl,ix ; ret ; ; Set a colour PrintColour cp 4 ; Above green don't make them bright jr nc,PrintCol1 ; or $40 ; Bright PrintCol1 ld (CurCol),a ; jr PrintExit ; ; Carriage return PrintCret call PrintDoCret ; jr PrintExit ; ; Clear screen - this clears it slowly with a fade effect PrintCls ld e,$00 ; ld hl,$5800 ; ld bc,$02FF ; pC1 ld a,(hl) ; and $07 ; jr z,pC2 ; set 0,e ; dec (hl) ; pC2 cpi ; jp pe,pC1 ; call Delay ; bit 0,e ; jr nz,PrintCls ; ld hl,$4000 ; ld de,$4001 ; ld bc,$1800 ; xor a ; ld (hl),a ; ldir ; ld a,(CurCol) ; ld (hl),a ; ld bc,$02FF ; ldir ; xor a ; out ($FE),a ; PrintHome xor a ; ld (pCurX),a ; ld (ppCurY),a ; ld a,cWhite ; ld (CurCol),a ; ld hl,$4000 ; ld (pCurY),hl ; jr PrintExit ; ; A suitable delay for the fade Delay push bc ; ld bc,1000 ; Del1 cpi ; dec hl ; jp pe,Del1 ; pop bc ; ret ; ; Do the carriage return PrintDoCret ld a,(ppCurY) ; inc a ; PrintSetUpY ld (ppCurY),a ; and $1F ; push hl ; ld l,a ; ld h,$00 ; add hl,hl ; ld de,YTable ; add hl,de ; ld a,(hl) ; inc hl ; ld h,(hl) ; ld l,a ; ld (pCurY),hl ; xor a ; ld (pCurX),a ; pop hl ; ret ; ; Output the character PrintOutChar bit 7,l ; characters $00..$7F only ret nz ; push hl ; ld h,0 ; add hl,hl ; add hl,hl ; add hl,hl ; ld de,PrintChars-256 ; add hl,de ; push hl ; pop ix ; ld a,(pCurX) ; rrca ; rrca ; rrca ; and $1F ; ld de,(pCurY) ; or e ; ld e,a ; push de ; ld a,d ; rrca ; rrca ; rrca ; and $03 ; or $58 ; ld d,a ; ld a,(CurCol) ; ld (de),a ; inc de ; ld (de),a ; pop de ; ld a,(pCurX) ; and $07 ; ld c,a ; ld b,$08 ; OutCharL ld h,(ix) ; ld l,$00 ; ld a,c ; or a ; jr z,OCL1 ; OCL2 srl h ; rr l ; dec a ; jr nz,OCL2 ; OCL1 ld a,(de) ; xor h ; ld (de),a ; inc e ; ld a,(de) ; xor l ; ld (de),a ; dec e ; inc ix ; inc d ; djnz OutCharL ; pop hl ; ret ; ; Screen addresses for the character rows YTable defw $4000,$4020,$4040,$4060 ; defw $4080,$40A0,$40C0,$40E0 ; defw $4800,$4820,$4840,$4860 ; defw $4880,$48A0,$48C0,$48E0 ; defw $5000,$5020,$5040,$5060 ; defw $5080,$50A0,$50C0,$50E0 ; defw 0,0,0,0,0,0,0,0 ; So running off the end isn't fatal ; Character set graphics align 256 ; Put these on page boundaries PrintChars defb $00,$00,$00,$00,$00,$00,$00,$00; defb $80,$80,$80,$80,$80,$00,$80,$00; defb $A0,$A0,$A0,$00,$00,$00,$00,$00; defb $50,$50,$F8,$50,$F8,$50,$50,$00; defb $20,$78,$A0,$70,$28,$F0,$20,$00; defb $C0,$C8,$10,$20,$40,$98,$18,$00; defb $40,$A0,$A0,$40,$A8,$90,$68,$00; defb $80,$80,$80,$00,$00,$00,$00,$00; defb $20,$40,$80,$80,$80,$40,$20,$00; defb $80,$40,$20,$20,$20,$40,$80,$00; defb $20,$A8,$70,$20,$70,$A8,$20,$00; defb $00,$20,$20,$F8,$20,$20,$00,$00; defb $00,$00,$00,$00,$40,$40,$80,$00; defb $00,$00,$00,$F8,$00,$00,$00,$00; defb $00,$00,$00,$00,$00,$00,$80,$00; defb $00,$08,$10,$20,$40,$80,$00,$00; defb $70,$88,$98,$A8,$C8,$88,$70,$00; defb $20,$60,$20,$20,$20,$20,$70,$00; defb $70,$88,$08,$30,$40,$80,$F8,$00; defb $F8,$08,$10,$30,$08,$88,$70,$00; defb $10,$30,$50,$90,$F8,$10,$10,$00; defb $F8,$80,$F0,$08,$08,$88,$70,$00; defb $38,$40,$80,$F0,$88,$88,$70,$00; defb $F8,$08,$10,$20,$40,$40,$40,$00; defb $70,$88,$88,$70,$88,$88,$70,$00; defb $70,$88,$88,$78,$08,$10,$E0,$00; defb $00,$00,$80,$00,$80,$00,$00,$00; defb $00,$00,$40,$00,$40,$40,$80,$00; defb $10,$20,$40,$80,$40,$20,$10,$00; defb $00,$00,$F8,$00,$F8,$00,$00,$00; defb $80,$40,$20,$10,$20,$40,$80,$00; defb $70,$88,$10,$20,$20,$00,$20,$00; defb $70,$88,$A8,$B8,$B0,$80,$78,$00; defb $20,$50,$88,$88,$F8,$88,$88,$00; defb $F0,$88,$88,$F0,$88,$88,$F0,$00; defb $70,$88,$80,$80,$80,$88,$70,$00; defb $F0,$88,$88,$88,$88,$88,$F0,$00; defb $F8,$80,$80,$F0,$80,$80,$F8,$00; defb $F8,$80,$80,$F0,$80,$80,$80,$00; defb $78,$80,$80,$80,$98,$88,$78,$00; defb $88,$88,$88,$F8,$88,$88,$88,$00; defb $E0,$40,$40,$40,$40,$40,$E0,$00; defb $08,$08,$08,$08,$08,$88,$70,$00; defb $88,$90,$A0,$C0,$A0,$90,$88,$00; defb $80,$80,$80,$80,$80,$80,$F8,$00; defb $88,$D8,$A8,$A8,$88,$88,$88,$00; defb $88,$88,$C8,$A8,$98,$88,$88,$00; defb $70,$88,$88,$88,$88,$88,$70,$00; defb $F0,$88,$88,$F0,$80,$80,$80,$00; defb $70,$88,$88,$88,$A8,$90,$68,$00; defb $F0,$88,$88,$F0,$A0,$90,$88,$00; defb $70,$88,$80,$70,$08,$88,$70,$00; defb $F8,$20,$20,$20,$20,$20,$20,$00; defb $88,$88,$88,$88,$88,$88,$70,$00; defb $88,$88,$88,$88,$88,$50,$20,$00; defb $88,$88,$88,$A8,$A8,$D8,$88,$00; defb $88,$88,$50,$20,$50,$88,$88,$00; defb $88,$88,$50,$20,$20,$20,$20,$00; defb $F8,$08,$10,$20,$40,$80,$F8,$00; defb $F8,$C0,$C0,$C0,$C0,$C0,$F8,$00; defb $00,$80,$40,$20,$10,$08,$00,$00; defb $F8,$18,$18,$18,$18,$18,$F8,$00; defb $20,$70,$A8,$20,$20,$20,$00,$00; defb $00,$00,$00,$00,$00,$00,$F8,$00; defb $00,$00,$00,$00,$00,$00,$00,$00; defb $00,$00,$68,$98,$88,$98,$68,$00; defb $80,$80,$B0,$C8,$88,$C8,$B0,$00; defb $00,$00,$78,$80,$80,$80,$78,$00; defb $08,$08,$68,$98,$88,$98,$68,$00; defb $00,$00,$70,$88,$F8,$80,$70,$00; defb $20,$40,$40,$E0,$40,$40,$40,$00; defb $00,$00,$78,$88,$98,$68,$08,$70; defb $80,$80,$F0,$88,$88,$88,$88,$00; defb $80,$00,$80,$80,$80,$80,$80,$00; defb $00,$20,$00,$20,$20,$20,$20,$C0; defb $80,$80,$90,$A0,$E0,$90,$88,$00; defb $C0,$40,$40,$40,$40,$40,$E0,$00; defb $00,$00,$D0,$A8,$A8,$A8,$A8,$00; defb $00,$00,$F0,$88,$88,$88,$88,$00; defb $00,$00,$70,$88,$88,$88,$70,$00; defb $00,$00,$F0,$88,$88,$F0,$80,$80; defb $00,$00,$78,$88,$88,$78,$08,$08; defb $00,$00,$B0,$C0,$80,$80,$80,$00; defb $00,$00,$78,$80,$70,$08,$F0,$00; defb $00,$20,$F8,$20,$20,$20,$18,$00; defb $00,$00,$88,$88,$88,$88,$78,$00; defb $00,$00,$88,$88,$50,$50,$20,$00; defb $00,$00,$88,$88,$88,$A8,$50,$00; defb $00,$00,$88,$50,$20,$50,$88,$00; defb $00,$00,$90,$90,$90,$F0,$10,$E0; defb $00,$00,$F8,$10,$20,$40,$F8,$00; defb $30,$40,$40,$80,$40,$40,$30,$00; defb $00,$80,$80,$00,$00,$80,$80,$00; defb $C0,$20,$20,$10,$20,$20,$C0,$00; defb $50,$00,$70,$88,$F8,$80,$70,$00; defb $00,$00,$08,$70,$80,$00,$00,$00; ; Character widths PrintWidths defb $07,$09,$09,$09,$09,$09,$09,$03; defb $05,$05,$05,$05,$03,$05,$05,$05; defb $05,$03,$05,$05,$05,$05,$09,$09 ; " " .. defb $07,$09,$07,$02,$05,$09,$07,$09; defb $06,$02,$04,$06,$06,$06,$06,$02; defb $04,$04,$06,$06,$03,$06,$02,$06; defb $06,$06,$06,$06,$06,$06,$06,$06; defb $06,$06,$02,$03,$05,$06,$05,$06; defb $06,$06,$06,$06,$06,$06,$06,$06; defb $06,$04,$06,$06,$06,$06,$06,$06; defb $06,$06,$06,$06,$06,$06,$06,$06; defb $06,$06,$06,$06,$06,$06,$06,$06; defb $06,$06,$06,$06,$06,$06,$04,$06; defb $06,$02,$04,$06,$04,$06,$06,$06; defb $06,$06,$05,$06,$06,$06,$06,$06; defb $06,$05,$06,$05,$02,$05,$06,$06; defb $03,$06,$0C,$12,$18,$1E,$24,$2A ; Wide spaces $80..? defb $30,$36,$3C,$42,$48,$06,$06,$06; defb $01,$02,$03,$04,$05,$06,$06,$06; defb $06,$06,$06,$06,$06,$06,$06,$06; defb $06,$06,$06,$06,$06,$06,$06,$06; defb $06,$06,$06,$06,$06,$06,$06,$06; defb $06,$06,$06,$06,$06,$06,$06,$06; defb $06,$06,$06,$06,$06,$06,$06,$06; defb $06,$06,$06,$06,$06,$06,$06,$06; defb $06,$06,$06,$06,$06,$06,$06,$06; defb $06,$06,$06,$06,$06,$06,$06,$06; defb $06,$06,$06,$06,$06,$06,$06,$06; defb $06,$06,$06,$06,$06,$06,$06,$06; defb $06,$06,$06,$06,$06,$06,$06,$06; defb $06,$06,$06,$06,$06,$06,$06,$06; defb $06,$06,$06,$06,$06,$06,$06,$06; ; Variables pCurX db 0 ; A logical X position ppCurY db 0 ; A logical Y position pCurY dw 0 ; The actual address CurCol db 0 ; The cursor colour ; Stop planting code after this. (When generating a tape file we save bytes below here) AppLast equ *-1 ; The last used byte's address ; Generate some useful debugging commands profile AppEntry,AppLast-AppEntry+1 ; Enable profiling for all the code ; Setup the emulation registers, so Zeus can emulate this code correctly Zeus_PC equ AppEntry ; Tell the emulator where to start Zeus_SP equ $FF46 ; Tell the emulator where to put the stack ; These generate some output files ; Generate a SZX file, these understand 128K files output_szx AppFilename+".szx",$0000,AppEntry ; The szx file ; Generate a Z80 file, these understand 128K files output_z80 AppFilename+".z80",$0000,AppEntry ; A z80 file ; We need to load a screen file here... I'm just using the one from Halls for now. import_bin "screen.scr",$4000 ; Now, here's a scatterloaded tzx using the new type 3 TZX... this first part is the same as a type 1 tzx build. ; ; This tells Zeus the PC filename, the tape filename and the tape comment, ; the start of the first memory block (AppEntry) ; the length of the first memory block (AppLast-AppEntry) ; the type of tzx (3) ; the execute address of this code (AppEntry) ; some rather garish border colours ($00123456) output_tzx "test.tzx","fred","bert",AppEntry,AppLast-AppEntry,3,AppEntry,$00123456 ; ; Now, we want to add some extra paged memory blocks to that. ; ; With a type 3 tzx we can load multiple blocks and we can have them from any page of memory ; ; We can either put the block list on the end of the "output_tzx" or we can have them on a separate "output_tzx_block" ; Statement. ; ; There's no difference, but doing it this way helps keep the lines shorter. ; ; Note that it doesn't matter if areas overlap, or what order you add them in. Zeus only loads each byte once. And ; doesn't load any specified bytes. ; ; So, let's specify the eight strings we have to save. ; ; Now, this looks like a mess. It is, a bit, I'm throwing you in at the deep end with this. It's very abstract coding ; ; These next bits add the strings that are stored in the 8 pages, one statement per string. ; ; <------- start address -> <-------- length of this block ---> ; output_tzx_block "test.tzx",CodePage_0.ZeusAddr+CodePage_0.String-$C000 , CodePage_0.StringEnd-CodePage_0.String ; ; Don't be thrown by the "name.name" format of these, it's just Zeus's way of addressing local labels from outside ; the procedure they're defined in. There are eight different procedure blocks, called "CodePage_0" to "CodePage_7" ; This name is entirely arbitrary, they could have been called "Mary", "Joe" or "RidiculouslyLongNameWithCamelCase" ; So "CodePage_0" is a procedure that I've put in page 0 (see org statement inside it) ; And "ZeusAddr" is just a label in it at the start to let us see where it starts ; "String" is a label at the start of each string. Note that these labels might be at different addresses ; Now, as far as the Z80 is concerned "String" needs to be an address in the range $C000..$FFFF, because that's where the ; paging hardware puts the pages in Z80 memory. We call the process "mapping"... ; ; This is saving the string in block 0... might be clearer if I put some numbers in. ; That line is the same as this: ; output_tzx_block "test.tzx", $10000 , 25 ; ; Where $10000 is the place in Zeus's memory where the 16K block for "page 0" starts, and 25 is just the string length. ; So, we now add these eight string to the tzx file "test.tzx" ; output_tzx_block "test.tzx",CodePage_0.ZeusAddr+CodePage_0.String-$C000,CodePage_0.StringEnd-CodePage_0.String output_tzx_block "test.tzx",CodePage_1.ZeusAddr+CodePage_1.String-$C000,CodePage_1.StringEnd-CodePage_1.String output_tzx_block "test.tzx",CodePage_2.ZeusAddr+CodePage_2.String-$C000,CodePage_2.StringEnd-CodePage_2.String output_tzx_block "test.tzx",CodePage_3.ZeusAddr+CodePage_3.String-$C000,CodePage_3.StringEnd-CodePage_3.String output_tzx_block "test.tzx",CodePage_4.ZeusAddr+CodePage_4.String-$C000,CodePage_4.StringEnd-CodePage_4.String output_tzx_block "test.tzx",CodePage_5.ZeusAddr+CodePage_5.String-$C000,CodePage_5.StringEnd-CodePage_5.String output_tzx_block "test.tzx",CodePage_6.ZeusAddr+CodePage_6.String-$C000,CodePage_6.StringEnd-CodePage_6.String output_tzx_block "test.tzx",CodePage_7.ZeusAddr+CodePage_7.String-$C000,CodePage_7.StringEnd-CodePage_7.String ; Warn the user if Zeus isn't up to the job... if ZEUSVER < 49 zeuserror "This version doesn't understand some of this source." zeuserror "You need Zeus v3.52 or above..." zeuserror "Please visit www.desdes.com/products/oldfiles for a free update." endif