diff --git a/README-ro.md b/README-ro.md new file mode 100644 index 0000000..13bcdb4 --- /dev/null +++ b/README-ro.md @@ -0,0 +1,53 @@ +# RUNZ80 pentru Cobra + +RUNZ80 este un utilitar CP/M pentru microcalculatorul Cobra care încărcă È™i +execută snapshot-uri ZX Spectrum în [formatul .z80][z80format] folosit de mulÈ›i +emulatori Spectrum. + +[z80format]: https://worldofspectrum.org/faq/reference/z80format.htm + +Suportă versiuni 1, 2, È™i 3 a formatului .z80, dar ignoră orice informaÈ›ie care +nu se aplică Spectrum 48K sau Cobra. + +## MulÈ›umiri + +Metoda de săltare în un snapshot de memorie È™i restaurare statul CPU-ului a +fost luat din [pagina BASIC+NMI a cobrasov.com][BASIC+NMI]. Codul în +LOADER.Z81 È™i RESTORE.Z80 a fost luat direct de acolo cu modificări minime. +Multe mulÈ›umiri autorului anonim al cobrasov.com! + +[BASIC+NMI]: http://cobrasov.com/CoBra%20Project/basic+nmi-ro.html + +## Rezumat + +`RUNZ80 SNAPSHOT[.Z80]` + +***NOTÄ‚: RUNZ80 va încerca să restaureze registrele cipului de sunet AY-3. Din +cauza conflictului de adrese între AY-3-ul Spectrumului 128K È™i interfaÈ›a de +floppy disk Cobra-ului, acest lucru poate cauza coruperea dischetelor dacă ei +nu sunt scoase mai întâi sau dacă unitățile nu sunt dezactivat după încărcarea +snapshot-ului.*** + +Când RUNZ80 a terminat să încarce fiÈ™ierul, se va întrerupe È™i va aÈ™tepta +intrare de tastatură. Odată ce apăsaÈ›i o tastă, va relua execuÈ›ia din +snapshot-ul. Pauza îți da oportunitatea să scoateÈ›i dischete din unități sau +le dezactivaÈ›i altfel să evitaÈ›i corupere. + +RUNZ80 execută o rutină pentru restaurare statul CPU-ului din fundul memoriei +video, deci când se încărcă un snapshot va exista niÈ™te gunoi în partea de sus +a ecranului. + +## Assamblând + +RUNZ80 a fost scris în asamblarea Z80 pentru SLR Systems Z80ASM Rel 1.32. +PuteÈ›i asambla propriul RUNZ80.COM rulând: + +`Z80ASM RUNZ80` + +## Ce e Cobra? + +(Este destul de obscur). Cobra este un calculator Românesc din anii 1980. +Este compatibil cu ZX Spectrum 48K dar poate rula È™i CP/M. Cele mai extinsă +sursă de informaÈ›ii este [cobrasov.com]. + +[cobrasov.com]: http://cobrasov.com diff --git a/README.md b/README.md new file mode 100644 index 0000000..d1b12d1 --- /dev/null +++ b/README.md @@ -0,0 +1,53 @@ +(Vezi [README-ro.md](README-ro.md) pentru o traducere greÈ™ită în română.) + +# RUNZ80 for Cobra + +RUNZ80 is a CP/M utility for the Cobra microcomputer which loads and executes +ZX Spectrum snapshots in the [.z80 format][z80format] used by many Spectrum +emulators. + +It supports .z80 snapshot versions 1, 2, and 3, but it ignores any information +that isn't applicable to Spectrum 48K or Cobra. + +[z80format]: https://worldofspectrum.org/faq/reference/z80format.htm + +## Acknowledgments + +The method for jumping into a memory snapshot and restoring CPU state was taken +from [the BASIC+NMI page of cobrasov.com][BASIC+NMI]. The code in LOADER.Z80 +and RESTORE.Z80 was taken directly from there with minimal modifications. Many +thanks to the anonymous author of cobrasov.com! + +[BASIC+NMI]: http://cobrasov.com/CoBra%20Project/basic+nmi.html + +## Synopsis + +`RUNZ80 SNAPSHOT[.Z80]` + +***NOTE: RUNZ80 attempts to restore AY-3 sound chip registers. Because of the +address conflict between the Spectrum 128K's AY-3 and the Cobra floppy +controller, this may cause corruption of floppy disks if they are not removed +or if the drives are not disabled after loading the snapshot.*** + +When RUNZ80 is finished loading the snapshot file it will pause and wait for +keyboard input. Once you press a key, it will resume execution from the +snapshot. The pause gives you an opportunity to remove your floppy disks from +the drives or otherwise disable them to avoid corruption. + +RUNZ80 runs the CPU restore routine from the bottom of video memory, so when a +snapshot starts, there will be some garbage at the top of the screen. + +## Building + +RUNZ80 was written in Z80 assembly for SLR Systems Z80ASM Rel 1.32. You can +assemble your own RUNZ80.COM by running: + +`Z80ASM RUNZ80` + +## What's Cobra? + +(It's pretty obscure). Cobra is a Romanian home computer from the 1980s. It's +compatible with the ZX Spectrum 48K but can also run CP/M. The most extensive +source of information is [cobrasov.com]. + +[cobrasov.com]: http://cobrasov.com diff --git a/src/GENRST.Z80 b/src/GENRST.Z80 new file mode 100644 index 0000000..e669823 --- /dev/null +++ b/src/GENRST.Z80 @@ -0,0 +1,83 @@ +; FILL IN RESTORE CODE VALUES FROM .Z80 HEADER +; DOES NOT USE V2/V3 PC + +GENRST: + LD DE,RST_BORDER + LD HL,BORDER + LDI + LD DE,RST_R + LD HL,HDR1_R + LDI + LD DE,RST_I + LD HL,HDR1_I + LDI + LD DE,RST_A_ + LD HL,HDR1_A_ + LDI + LD DE,RST_F_ + LD HL,HDR1_F_ + LDI + LD DE,RST_A + LD HL,HDR1_A + LDI + LD DE,RST_F + LD HL,HDR1_F + LDI + LD DE,RST_BC + LD HL,HDR1_BC + LDI + LDI + LD DE,RST_DE + LD HL,HDR1_DE + LDI + LDI + LD DE,RST_HL + LD HL,HDR1_HL + LDI + LDI + LD DE,RST_SP + LD HL,HDR1_SP + LDI + LDI + LD DE,RST_IX + LD HL,HDR1_IX + LDI + LDI + LD DE,RST_IY + LD HL,HDR1_IY + LDI + LDI + LD DE,RST_BC_ + LD HL,HDR1_BC_ + LDI + LDI + LD DE,RST_DE_ + LD HL,HDR1_DE_ + LDI + LDI + LD DE,RST_HL_ + LD HL,HDR1_HL_ + LDI + LDI + LD DE,RST_EIDI + LD A,(HDR1_IFF1) + AND A + JR Z,GENRST_DI ; ALREADY DI + LD A,0FBH ; EI + LD (DE),A +GENRST_DI: + LD DE,RST_IM + LD A,(HDR1_FLAGSB) + AND 03H ; INTERRUPT MODE + LD C,A + LD B,0 + LD HL,GENRST_IMS ; IM SECOND BYTE TABLE + ADD HL,BC + LDI + LD DE,RST_PC + LD HL,HDR1_PC + LDI + LDI + RET + +GENRST_IMS: DB 46H,56H,5EH diff --git a/src/HEADER.Z80 b/src/HEADER.Z80 new file mode 100644 index 0000000..3d51ba0 --- /dev/null +++ b/src/HEADER.Z80 @@ -0,0 +1,41 @@ +; .Z80 SNAPSHOT HEADER +; DOCUMENTED AT https://worldofspectrum.org/faq/reference/z80format.htm + +HEADER_LEN EQU 87 + +HEADER1: +HDR1_A: DB 0 +HDR1_F: DB 0 +HDR1_BC: DW 0 +HDR1_HL: DW 0 +HDR1_PC: DW 0 +HDR1_SP: DW 0 +HDR1_I: DB 0 +HDR1_R: DB 0 +HDR1_FLAGSA: DB 0 +HDR1_DE: DW 0 +HDR1_BC_: DW 0 +HDR1_DE_: DW 0 +HDR1_HL_: DW 0 +HDR1_A_: DB 0 +HDR1_F_: DB 0 +HDR1_IY: DW 0 +HDR1_IX: DW 0 +HDR1_IFF1: DB 0 +HDR1_IFF2: DB 0 +HDR1_FLAGSB: DB 0 + +HEADER23: ; ADDITIONAL HEADER FOR VERSION 2/3 +HDR2_LEN: DW 0 +HDR2_PC: DW 0 +HDR2_HWMODE: DB 0 + DB 0 ; NOT FOR COBRA + DB 0 ; NOT FOR COBRA +HDR2_FLAGSC: DB 0 +HDR2_OUTFFFD: DB 0 +HDR2_SNDREGS: DB 0,0,0,0,0,0,0,0 + DB 0,0,0,0,0,0,0,0 +HDR2_TLO: DW 0 +HDR2_THI: DB 0 + DS 28 ; NOT FOR COBRA +HDR2_OUT1FFD: DB 0 diff --git a/src/LOADER.Z80 b/src/LOADER.Z80 new file mode 100644 index 0000000..61c1ed4 --- /dev/null +++ b/src/LOADER.Z80 @@ -0,0 +1,73 @@ +; THIS CODE WAS COPIED ALMOST ENTIRELY FROM COBRASOV.COM. +; +; THE ORIGINAL CODE IS EXTENSIVELY DOCUMENTED HERE: +; http://cobrasov.com/CoBra%20Project/basic+nmi-ro.html +; http://cobrasov.com/CoBra%20Project/basic+nmi.html +; +; THE ROUTINE COPIES SNAPSHOT MEMORY INTO PLACE BEFORE JUMPING TO THE RESTORE +; ROUTINE IN SPECTRUM-COMPTABILITY MODE. I HAVE MODIFIED IT TO WORK WITH +; RUNZ80 AND TO RESTORE AY-3 STATE AND BORDER COLOR. + +LOADER: + DI + LD HL,Z80MEMEND + LD DE,0FFFFH + LD BC,0C000H + LDDR + LD BC,1B00H + LD HL,4000H +LOADER_LOOP1: + LD D,(HL) + LD A,40H + OUT (0FEH),A + LD (HL),D + XOR A + OUT (0FEH),A + INC HL + DEC BC + LD A,B + OR C + JR NZ,LOADER_LOOP1 + LD A,03H + OUT (0E3H),A + OUT (0EBH),A + OUT (0F3H),A + OUT (0FBH),A + XOR A + OUT (0FDH),A + LD A,(BORDER) ; SET BORDER + OUT (0FEH),A +LOADER_LOOP2: + LD A,0 + IN A,(0FEH) + AND 3FH + CP 3FH + JR NZ,LOADER_SKIP + JR LOADER_LOOP2 +LOADER_SKIP: + LD HL,AY3REGS+16 ; RESTORE AY3 REGISTERS + LD A,16 +LOADER_AY3LOOP: + DEC HL + DEC A + LD D,(HL) + LD BC,0FFFDH + OUT (C),A + LD BC,0BFFDH + OUT (C),D + AND A + JR NZ,LOADER_AY3LOOP + DEC HL + LD A,(HL) + LD BC,0FFFDH + OUT (C),A + LD BC,0 + LD SP,Z80RESTORE ; MOVING ON + LD HL,Z80RESTORE + EXX + LD HL,0038H + LD A,(BORDER) + OR 0C0H + OUT (0FEH),A + LD R,A + JP (HL) diff --git a/src/RESTORE.Z80 b/src/RESTORE.Z80 new file mode 100644 index 0000000..ef08f17 --- /dev/null +++ b/src/RESTORE.Z80 @@ -0,0 +1,60 @@ +; THIS CODE WAS COPIED ALMOST ENTIRELY FROM COBRASOV.COM. +; +; THE ORIGINAL CODE IS EXTENSIVELY DOCUMENTED HERE: +; http://cobrasov.com/CoBra%20Project/basic+nmi-ro.html +; http://cobrasov.com/CoBra%20Project/basic+nmi.html +; +; I HAVE MODIFIED THE ORIGINAL WITH PLACEHOLDER VALUES (0) SO THAT IT CAN BE +; ASSEMBLED. THE CODE IN GENRST.Z80 MODIFIES THE MACHINE CODE AT RUNTIME TO +; FILL IN THE REGISTER VALUES FROM THE SNAPSHOT. + +RESTORE: +_RST_BORDER: LD A,0 + OUT (0FEH),A +_RST_R: LD A,0 + LD R,A +_RST_I: LD A,0 + LD I,A +_RST_AF_: LD HL,0 + PUSH HL +_RST_AF: LD HL,0 + PUSH HL + POP AF + EX AF,AF' + POP AF + EX AF,AF' +_RST_BC: LD BC,0 +_RST_DE: LD DE,0 +_RST_HL: LD HL,0 +_RST_SP: LD SP,0 +_RST_IX: LD IX,0 +_RST_IY: LD IY,0 + EXX +_RST_BC_: LD BC,0 +_RST_DE_: LD DE,0 +_RST_HL_: LD HL,0 + EXX +RST_EIDI: DI +_RST_IM: IM 0 +_RST_PC: JP 0 + +; ADDRESSES OF OPERANDS IN THE ASSEMBLED CODE +RST_BORDER EQU _RST_BORDER + 1 +RST_R EQU _RST_R + 1 +RST_I EQU _RST_I + 1 +RST_F_ EQU _RST_AF_ + 1 +RST_A_ EQU _RST_AF_ + 2 +RST_F EQU _RST_AF + 1 +RST_A EQU _RST_AF + 2 +RST_BC EQU _RST_BC + 1 +RST_DE EQU _RST_DE + 1 +RST_HL EQU _RST_HL + 1 +RST_SP EQU _RST_SP + 1 +RST_IX EQU _RST_IX + 2 +RST_IY EQU _RST_IY + 2 +RST_BC_ EQU _RST_BC_ + 1 +RST_DEÿ_ EQU _RST_DE_ + 1 +RST_HL_ EQU _RST_HL_ + 1 +RST_IM EQU _RST_IM + 1 +RST_PC EQU _RST_PC + 1 +RESTOREEND: diff --git a/src/RUNZ80.Z80 b/src/RUNZ80.Z80 new file mode 100644 index 0000000..fc53256 --- /dev/null +++ b/src/RUNZ80.Z80 @@ -0,0 +1,359 @@ +; CP/M UTILITY FOR COBRA TO LOAD AND EXECUTE A .Z80 +; SNAPSHOT FILE +; +; USAGE: +; RUNZ80 FILE[.Z80] + + +; BDOS ENTRY POINT +BDOS EQU 5 + +; BDOS FUNCTIONS +SYSRESET EQU 0 +CONOUT EQU 2 +PRINTS EQU 9 +OPEN EQU 15 +READSEQ EQU 20 + +; FILE ARGUMENT FCB +FCB1 EQU 005CH +; DESTINATION OF FILE LOADS +DMA EQU 0080H + + +; LOCATION OF RESTORE ROUTINE IN ZX SPECTRUM MEMORY +; VRAM PLUS 4 BYTES OF STACK FOR THE RESTORE ROUTINE +Z80RESTORE EQU 4004H + +; LOAD ADDRESS FOR SNAPSHOT MEMORY. MUST BE HIGH ENOUGH FOR +; (MOST OF) THIS PROGRAM TO FIT UNDERNEATH IT. SOME OF IT +; IS USED FOR TEMPORARY STORAGE - SEE ORG Z80MEM. +Z80MEM EQU 0600H +; END OF SNAPSHOT MEMORY +Z80MEMEND EQU Z80MEM + 0BFFFH + + +ORG 100H + +START: + LD DE,FCB1+9 ; ADD "Z80" EXTENSION + LD HL,EXT + LDI + LDI + LDI + CALL OPENFILE + CALL LOADZ80 + CALL COPYRESTORE + JP LOADER +EXIT: + LD C,SYSRESET + JP BDOS +ERROR: + LD DE,ERRORMSG + LD C,PRINTS + CALL BDOS + JR EXIT + +EXT: DB 'Z80' + + +; OPEN FILE SPECIFIED BY FCB1 +OPENFILE: + LD C,OPEN ; OPEN FILE ARGUMENT + LD DE,FCB1 + CALL BDOS + SUB 0FFH ; FF INDICATES ERROR + RET NZ + LD C,PRINTS ; PRINT ERROR AND QUIT + LD DE,EOPENMSG + CALL BDOS + JP EXIT + + +; READ THE NEXT BLOCK FROM FILE SPECIFIED BY FCB1 INTO DMA +READBLOCK: + LD C,READSEQ + LD DE,FCB1 + CALL BDOS + AND A + RET Z + JP ERROR + +; LOAD A .Z80 SNAPSHOT FROM AN OPEN FILE +LOADZ80: + CALL READBLOCK ; LOAD HEADERS + LD HL,DMA ; COPY HEADERS + LD DE,HEADERS + LD BC,87 + LDIR + LD A,(HDR1_FLAGSA) ; GET BIT 7 OF R + RRCA + AND 80H + LD HL,HDR1_R + OR (HL) + LD (HL),A + LD A,(HDR1_FLAGSA) ; GET BORDER + RRCA + AND 07H + LD (BORDER),A + CALL GENRST ; GENERATE RESTORE CODE FROM HEADER + LD HL,(HDR1_PC) ; CHECK VERSION AND LOAD MEMORY SNAPSHOT + LD A,H + AND A + JP NZ,LOADV1 + LD A,L + AND A + JP NZ,LOADV1 + JP LOADV23 + + +; INDIRECT JUMP THROUGH IX. USED TO DYNAMICALLY CALL +; LOADBYTE OR LOADBYTE_COUNTED DEPENDING ON VERSION OF THE +; SNAPSHOT FILE. V2/V3 SPECIFY THE LENGTH OF MEMORY BLOCKS +; (LOADBYTE_COUNTED). V1 DOES NOT (LOADBYTE). +JPIX: + JP (IX) + +; RETURN FROM THE CALLING FUNCTION. USED BY +; LOADBYTE_COUNTED TO RETURN FROM LOADUNCOMP/LOADCOMP/SEEK +; WHEN THE END OF THE BLOCK IS REACHED. +RETCALLER: + INC SP + INC SP + RET + +; WRAP A DECREMENT AND COMPARE AROUND LOADBYTE TO RUN +; LOADBYTE BC TIMES +LOADBYTE_COUNTED: + LD A,B ; CHECK BC FOR 0 + OR C + JR Z,RETCALLER ; IF 0 RETURN FROM CALLER + DEC BC + +; LOAD NEXT BYTE INTO A, RELOADING BUFFER FROM DISK WHEN +; NECESSARY +LOADBYTE: + LD A,L ; SEE IF WE HAVE OVERFLOWED + AND A + JR NZ,LOADBYTE_RET + PUSH BC ; SAVE REGS + PUSH DE + PUSH IX + LD C,READSEQ ; LOAD ANOTHER FROM DISK IF SO + LD DE,FCB1 + CALL BDOS + POP IX + POP DE + POP BC + AND A ; CHECK READSEQ RETURN + JP NZ,ERROR + LD HL,DMA ; RETURN TO START OF DMA +LOADBYTE_RET: + LD A,(HL) + INC L + RET + +; LOAD BC BYTES OF UNCOMPRESSED SNAPSHOT MEMORY +LOADUNCOMP: + CALL LOADBYTE_COUNTED + LD (DE),A + INC DE + JR LOADUNCOMP + +; LOAD AND UNCOMPRESS COMPRESSED DATA UNTIL END OF BLOCK +LOADCOMPV1: + LD IX,LOADBYTE + JR LOADCOMP +; LOAD AND UNCOMPRESS BC BYTES OF COMPRESSED DATA +LOADCOMPV23: + LD IX,LOADBYTE_COUNTED +; LOAD AND UNCOMPRESS COMPRESSED DATA +LOADCOMP: + CALL JPIX ; GET NEXT BYTE + CP 0EDH ; CHECK FOR FIRST ED + JR NZ,LOADCOMP_NOED + LD (DE),A ; STORE THE FIRST ED + INC DE + CALL JPIX ; GET NEXT BYTE + CP 0EDH ; CHECK FOR SECOND ED + JR NZ,LOADCOMP_NOED + CALL JPIX ; FOUND SECOND DE, LOAD N + AND A ; CHECK FOR N=0, END OF BLOCK + RET Z ; END OF BLOCK FOUND + DEC A + LD (FILLCOUNT),A + CALL JPIX ; LOAD X, A = X + DEC DE ; REWIND TO FIRST ED + PUSH BC ; FILL (FILLCOUNT) WITH (FILLVALUE) + PUSH AF ; SAVE FILL VALUE + LD A,(FILLCOUNT) + LD B,A + POP AF ; RESTORE FILL VALUE +LOADCOMP_FILL: + LD (DE),A + INC DE + DJNZ LOADCOMP_FILL + POP BC +LOADCOMP_NOED: + LD (DE),A ; STORE A NON-ED BYTE + INC DE + JR LOADCOMP + +FILLCOUNT: DB 0 + +; SKIP BC BYTES OF THE FILE +SEEK: + CALL LOADBYTE_COUNTED + JR SEEK + +; LOAD A V1 .Z80 FILE +LOADV1: + LD DE,V1MSG + LD C,PRINTS + CALL BDOS + LD DE,Z80MEM ; LOAD REST OF BLOCK 1 + LD HL,DMA + 30 + LD A,(HDR1_FLAGSA) ; CHECK IF COMPRESSED + BIT 5,A + JP NZ,LOADCOMPV1 + LD BC,48*1024 + JP LOADUNCOMP + +; LOAD A V2/V3 .Z80 FILE +LOADV23: + LD DE,V23MSG + LD C,PRINTS + CALL BDOS + LD DE,RST_PC ; USE PC FROM ADDITIONAL HEADER IN RESTORE + LD HL,HDR2_PC + LDI + LDI + PUSH HL ; GET AY-3 REGS FROM HEADER + LD DE,AY3HEADER + LD HL,DMA+38 + LD BC,17 + LDIR + POP HL + LD HL,DMA+32 ; SEEK PAST HEADERS + LD BC,(DMA+30) + CALL SEEK + CALL LOADPAGE + CALL LOADPAGE + CALL LOADPAGE + RET + +; LOAD A V2/V3 PAGE +LOADPAGE: + CALL LOADBYTE ; READ LENGTH INTO BC + LD C,A + CALL LOADBYTE + LD B,A + CALL LOADBYTE ; READ PAGE NUMBER INTO A + CP 4 ; SET DE=DEST AND LOAD IF PAGE 4, 5, or 8 + JR NZ,LOADPAGE_NOT4 + LD DE,8000H-4000H+0600H + JR LOADPAGE_LOAD +LOADPAGE_NOT4: + CP 5 + JR NZ,LOADPAGE_NOT5 + LD DE,0C000H-4000H+0600H + JR LOADPAGE_LOAD +LOADPAGE_NOT5: + CP 8 + JR NZ,LOADPAGE_NOT8 + LD DE,4000H-4000H+0600H + JR LOADPAGE_LOAD +LOADPAGE_NOT8: + PUSH HL ; SKIP TO NEXT PAGE AND TRY TO LOAD + LD HL,SKIPMSG + CALL PAGESTATUS + POP HL + LD A,B ; CHECK BC FOR FFFF + AND C + CPL + AND A + JR NZ,LOADPAGE_SEEK + LD BC,16384 +LOADPAGE_SEEK: + CALL SEEK + JR LOADPAGE +LOADPAGE_LOAD: + PUSH HL ; PRINT LOADING MESSAGE + LD HL,LOADMSG + CALL PAGESTATUS + POP HL + LD A,B ; CHECK BC FOR FFFF + AND C + CPL + AND A + JP NZ,LOADCOMPV23 ; LOAD COMPRESSED IF NOT FFFF + LD BC,16384 ; LOAD UNCOMPRESSED IF FFFF + JP LOADUNCOMP + +; PRINT MESSAGE FROM HL PLUS PAGE NUMBER IN HEX +PAGESTATUS: + PUSH BC + PUSH DE + PUSH AF + LD C,PRINTS + PUSH HL + POP DE + CALL BDOS + POP AF + PUSH AF + CP 0AH + JP C,PAGESTATUS_NUMBER + ADD 7 +PAGESTATUS_NUMBER: + ADD 30H + LD E,A + LD C,CONOUT + CALL BDOS + LD E,0DH + LD C,CONOUT + CALL BDOS + LD E,0AH + LD C,CONOUT + CALL BDOS + POP AF + POP DE + POP BC + RET + + +; COPY GENERATED RESTORE INTO SNAPSHOT MEMORY +COPYRESTORE: + LD DE,Z80RESTORE - 4000H + Z80MEM + LD HL,RESTORE + LD BC,RESTOREEND-RESTORE + LDIR + RET + + +; STATUS/ERROR STRINGS +EOPENMSG: DB 'File not found',0DH,0AH,'$' +V1MSG: DB 'Detected version 1',0DH,0AH,'$' +V23MSG: DB 'Detected version 2+',0DH,0AH,'$' +ERRORMSG: DB 'Unexpected end of file',0DH,0AH,'$' +SKIPMSG: DB 'Skipping page $' +LOADMSG: DB 'Loading page $' + +; AY3 HEADER FROM V2/V3 HEADER +AY3HEADER: +LASTFFFD: DB 0 +AY3REGS: DS 16,0 + +; BORDER COLOR FROM HEADER +BORDER: DB 0 + +INCLUDE RESTORE.Z80 +INCLUDE LOADER.Z80 + +; MEMORY PAST HERE IS OVERWRITTEN WITH SNAPSHOT MEMORY +; AFTER HEADERS ARE PROCESSED +ORG Z80MEM +HEADERS: +INCLUDE HEADER.Z80 +INCLUDE GENRST.Z80 + +END