commit 18eb40eda824255fd3747ea1d1cb63b356ba20e4 Author: Martin Haye Date: Sat Jul 20 08:42:23 2013 -0700 Re-initializing repo leaving out game assets. diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c4e6948 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +*.class +*.2mg +*.swp +ASSETS diff --git a/a2copy/.classpath b/a2copy/.classpath new file mode 100644 index 0000000..75b1101 --- /dev/null +++ b/a2copy/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/a2copy/.project b/a2copy/.project new file mode 100644 index 0000000..132becd --- /dev/null +++ b/a2copy/.project @@ -0,0 +1,17 @@ + + + a2copy + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/a2copy/a2copy.jar b/a2copy/a2copy.jar new file mode 100644 index 0000000..1381962 Binary files /dev/null and b/a2copy/a2copy.jar differ diff --git a/a2copy/a2copy.jardesc b/a2copy/a2copy.jardesc new file mode 100644 index 0000000..1f5cefa --- /dev/null +++ b/a2copy/a2copy.jardesc @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/a2copy/lib/VERSIONS b/a2copy/lib/VERSIONS new file mode 100644 index 0000000..084b131 --- /dev/null +++ b/a2copy/lib/VERSIONS @@ -0,0 +1 @@ +ac.jar = AppleCommander-1.3.5.13-ac.jar with Martn's patch for proper sub-directory creation diff --git a/a2copy/lib/ac.jar b/a2copy/lib/ac.jar new file mode 100644 index 0000000..c5748fb Binary files /dev/null and b/a2copy/lib/ac.jar differ diff --git a/a2copy/src/A2copy.java b/a2copy/src/A2copy.java new file mode 100644 index 0000000..ad2501f --- /dev/null +++ b/a2copy/src/A2copy.java @@ -0,0 +1,321 @@ +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.BufferedReader; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.PrintWriter; +import java.util.List; +import java.util.zip.GZIPInputStream; + +import com.webcodepro.applecommander.storage.DirectoryEntry; +import com.webcodepro.applecommander.storage.Disk; +import com.webcodepro.applecommander.storage.DiskFullException; +import com.webcodepro.applecommander.storage.FileEntry; +import com.webcodepro.applecommander.storage.FormattedDisk; + +/* + * This class uses AppleCommander's command-line interface to extract an entire + * set of files and directories from an image, or build a whole image from + * files and directories. + * + * Has specific hard-coded addresses for Lawless Legends files; should fix this + * at some point to use a metadata configuration file or something like that. + */ +public class A2copy +{ + /* + * Main command-line driver + */ + public static void main(String[] args) + throws IOException, DiskFullException + { + try + { + if (args[0].equals("-extract")) { + extractImg(args[1], args[2]); + return; + } + if (args[0].equals("-create")) { + createImg(args[1], args[2]); + return; + } + } + catch (ArrayIndexOutOfBoundsException e) + { } + System.err.format("Usage: A2copy [-extract imgFile targetDir] | [-create imgFile sourceDir]\n"); + System.exit(1); + } + + /** + * Helper to delete a file or directory and all its descendants. + * @throws IOException if something goes wrong + */ + static void delete(File f) throws IOException { + if (f.isDirectory()) { + for (File c : f.listFiles()) + delete(c); + } + if (!f.delete()) + throw new FileNotFoundException("Failed to delete file: " + f); + } + + /** + * Extract all the files and directories from an image file. + * @throws IOException if something goes wrong + */ + @SuppressWarnings("unchecked") + static void extractImg(String imgPath, String targetPath) throws IOException + { + // Ensure the target directory is empty first. + File targetDir = new File(targetPath); + if (targetDir.exists()) { + System.out.format("Note: %s will be overwritten. Continue? ", targetPath); + System.out.flush(); + String response = new BufferedReader(new InputStreamReader(System.in)).readLine(); + if (!response.toLowerCase().startsWith("y")) + return; + delete(targetDir); + } + + // Open the image file and process disks inside it. + Disk disk = new Disk(imgPath); + for (FormattedDisk fd : disk.getFormattedDisks()) + extractFiles((List)fd.getFiles(), targetDir); + } + + /** + * Helper for file/directory extraction. + * @param files set of files to extract + * @param targetDir where to put them + * @throws IOException if something goes wrong + */ + @SuppressWarnings("unchecked") + static void extractFiles(List files, File targetDir) throws IOException + { + // Ensure the target directory exists + targetDir.mkdir(); + + // Process each entry in the list + for (FileEntry e : files) + { + // Skip deleted things + if (e.isDeleted()) + continue; + + // Recursively process sub-directories + if (e.isDirectory()) { + File subDir = new File(targetDir, e.getFilename()); + extractFiles(((DirectoryEntry)e).getFiles(), subDir); + continue; + } + + // Process regular files. + byte[] data = e.getFileData(); + // Hi to lo ASCII translation + if (e.getFilename().endsWith(".S")) + data = merlinSrcToAscii(data); + FileOutputStream out = new FileOutputStream(new File(targetDir, e.getFilename())); + out.write(data); + out.close(); + } + } + + /** + * Creates an image file using dirs/files from the filesystem. + * @param imgPath name of the image file + * @param srcDirPath directory containing files and subdirs + * @throws DiskFullException if the image file fills up + * @throws IOException if something else goes wrong + */ + static void createImg(String imgPath, String srcDirPath) + throws IOException, DiskFullException + { + // Alert the user that we're going to blow away the image. + File imgFile = new File(imgPath); + if (imgFile.exists()) { + System.out.format("Note: %s will be overwritten. Continue? ", imgPath); + System.out.flush(); + String response = new BufferedReader(new InputStreamReader(System.in)).readLine(); + if (!response.toLowerCase().startsWith("y")) + return; + delete(imgFile); + } + + // Re-create the image file with a blank image. + File emptyFile = new File(imgFile.getParent(), "empty.2mg.gz"); + if (!emptyFile.canRead()) + throw new IOException(String.format("Cannot open template for empty image '%s'", emptyFile.toString())); + InputStream in = new BufferedInputStream(new GZIPInputStream(new FileInputStream(emptyFile))); + OutputStream out = new BufferedOutputStream(new FileOutputStream(imgFile)); + byte[] buf = new byte[1024]; + while (true) { + int nRead = in.read(buf); + if (nRead < 0) + break; + out.write(buf, 0, nRead); + } + in.close(); + out.close(); + + // Open the empty image file. + Disk disk = new Disk(imgPath); + FormattedDisk fd = disk.getFormattedDisks()[0]; + + // And fill it up. + insertFiles(fd, fd, new File(srcDirPath)); + } + + /** + * Helper for image creation. + * + * @param fd disk to insert files into + * @param targetDir directory within the disk + * @param srcDir filesystem directory to read + * @throws DiskFullException if the image file fills up + * @throws IOException if something else goes wrong + */ + private static void insertFiles(FormattedDisk fd, DirectoryEntry targetDir, File srcDir) + throws DiskFullException, IOException + { + // Process each file in the source directory + for (File srcFile : srcDir.listFiles()) + { + if (srcFile.isDirectory()) { + DirectoryEntry subDir = targetDir.createDirectory(srcFile.getName().toUpperCase()); + insertFiles(fd, subDir, srcFile); + continue; + } + + // Create a new entry on the filesystem for this file. + FileEntry ent = targetDir.createFile(); + String srcName = srcFile.getName().toUpperCase(); + ent.setFilename(srcName); + + // Set the file type + if (srcName.equals("PRODOS") || srcName.endsWith(".SYSTEM")) + ent.setFiletype("SYS"); + else if (srcName.equals("STARTUP")) + ent.setFiletype("BAS"); + else if (srcName.endsWith(".S")) + ent.setFiletype("TXT"); + else + ent.setFiletype("BIN"); + + // Set the address if necessary + if (ent.needsAddress()) { + if (srcName.equals("STARTUP")) + ent.setAddress(0x801); + else if (srcName.equals("COPYIIPL.SYSTEM")) + ent.setAddress(0x1400); + else if (srcName.equals("ED.16")) + ent.setAddress(0x9d60); + else if (srcName.equals("ED")) + ent.setAddress(0x9db6); + else if (srcName.equals("SHELL")) + ent.setAddress(0x300); + else + ent.setAddress(0x2000); + } + + // Copy the file data + FileInputStream in = new FileInputStream(srcFile); + byte[] buf = new byte[(int) srcFile.length()]; + int nRead = in.read(buf); + if (nRead != srcFile.length()) + throw new IOException(String.format("Error reading file '%s'", srcFile.toString())); + + // Translate between hi and lo ASCII + if (srcName.endsWith(".S")) + buf = asciiToMerlinSrc(buf); + ent.setFileData(buf); + + // And save the new entry. + fd.save(); + } + } + + /** + * Translates Merlin source code to usable code in the regular ASCII world. + * Performs weird-space to tab translation, and hi bit conversion. + * + * @param buf data to translate + * @return translated data + */ + static byte[] merlinSrcToAscii(byte[] buf) + { + ByteArrayOutputStream ba = new ByteArrayOutputStream(buf.length); + PrintWriter out = new PrintWriter(ba); + boolean inComment = false; + for (byte b : buf) + { + // Handle newlines + if (b == (byte)0x8d) { + out.println(); + inComment = false; + } + else { + char c = (char)(b & 0x7f); + // Tabs outside comments + if (c == ';' || c == '*') + inComment = true; + if (c == '\n') + throw new RuntimeException("Newline slipped through"); + if (c == ' ' && !inComment) + out.write('\t'); + else + out.write(c); + } + } + out.flush(); + return ba.toByteArray(); + } + + /** + * Transforms regular ASCII with tabs to Merlin source code. Handles + * tab to weird space translation, and hi-bit addition. + * + * @param buf data to translate + * @return translated data + */ + static byte[] asciiToMerlinSrc(byte[] buf) + { + ByteArrayOutputStream ba = new ByteArrayOutputStream(buf.length); + boolean inComment = false; + for (int i=0; iTEX0,>TEX1,>TEX2,>TEX3 + ; mip level offsets +MIPOFFL DFB <0,<4096,<5120,<5376,<5440,<5456,<5460 +MIPOFFH DFB >0,>4096,>5120,>5376,>5440,>5456,>5460 + +NEXTLINE LDA PLINE+1 ; Hi byte of line + CLC + ADC #4 ; Next line is 1K up + TAX + EOR PLINE+1 + AND #$20 ; Past end of screen? + BEQ :DONE ; If not, we're done + TXA + SEC + SBC #$20 ; Back to start + TAX + LDA PLINE ; Lo byte + CLC + ADC #$80 ; Inner blks offset by 128 bytes + STA PLINE + BCC :DONE + INX ; Next page + TXA + AND #7 + CMP #4 ; Still inside inner blk? + BNE :DONE ; If so we're done + TXA + SEC + SBC #4 ; Back to start of inner blk + TAX + LDA PLINE + CLC + ADC #$28 ; Outer blks offset by 40 bytes + STA PLINE +:DONE STX PLINE+1 + RTS + +* Template for blitting code + +BLITTPL ; comments show byte offset +* even rows + LDA SH0157 ; 0: pixel 3 + ASL ; 3: save half of pix 3 in carry + ORA SH0101 ; 4: pixel 0 + ORA SH0123 ; 7: pixel 1 + ORA SH0145 ; 10: pixel 2 + STA (0),Y ; 13: even column + INY ; 15: prep for odd + LDA SH0101 ; 16: pixel 4 + ORA SH0123 ; 19: pixel 5 + ROL ; 22: recover half of pix 3 + ORA SH0156 ; 23: pixel 6 - after rol to ensure right hi bit + STA (0),Y ; 26: odd column + DEY ; 28: prep for even +* odd rows + LDA SH4557 ; 29: pixel 3 + ASL ; 32: save half of pix 3 in carry + ORA SH4501 ; 33: pixel 0 + ORA SH4523 ; 36: pixel 1 + ORA SH4545 ; 39: pixel 2 + STA (2),Y ; 42: even column + INY ; 44: prep for odd + LDA SH4501 ; 45: pixel 4 + ORA SH4523 ; 48: pixel 5 + ROL ; 51: recover half of pix 3 + ORA SH4556 ; 52: pixel 6 - after rol to ensure right hi bit + STA (2),Y ; 55: odd column + DEY ; 57: prep for even + ; 58: total + +* Create the unrolled blit code +MAKEBLIT + LDA #0 ; Start with line zero + STA LINECT + LDA #TOPLINE + STA PLINE+1 + LDA #BLITROLL + STA PDST+1 +:LINELUP +; Copy the template + LDY #57 +:COPY LDA BLITTPL,Y + STA (PDST),Y + DEY + BPL :COPY + ; Record the address for the even line + JSR :STIDX +; Set the even line pointers + LDY #14 + JSR :STLINE + LDY #27 + JSR :STLINE + ; Get ready for odd line + JSR :ADVANCE + ; Record the address for the odd line + JSR :STIDX +; Set the odd line pointers + LDY #14 + JSR :STLINE + LDY #27 + JSR :STLINE + ; Prepare for next iteration + JSR :ADVANCE +; Loop until all lines are done + LDA LINECT + CMP #NLINES + BNE :LINELUP + JSR :STIDX ; Last addr to index + JMP STRTS ; Finish with RTS for cleanliness +:STLINE ; Subroutine to store PLINE to PDST + LDA LINECT + ASL + STA (PDST),Y + RTS +:STIDX ; Subroutine to store tbl ptr to index + LDY LINECT + LDA PDST + STA BLITIDXL,Y + LDA PDST+1 + STA BLITIDXH,Y + RTS +:ADVANCE ; Subroutine to go to next unroll + LDA #29 + JSR ADVPDST + INC LINECT + JMP NEXTLINE + +* Create code to clear the blit +MAKECBLIT + LDX #0 + LDY #0 +:LUP LDA :ST + STA CBLITROLL,X + INX + LDA BLITIDXL,Y + STA CBLITROLL,X + INX + LDA BLITIDXH,Y +:ST STA CBLITROLL,X + INX + INY + INY + CPY #NLINES + BNE :LUP + LDA :RTS + STA CBLITROLL,X +:RTS RTS + +* Clear the blit +CLRBLIT + LDA #0 +CLRBLIT2 + LDX BLITOFFE+0 + JSR CBLITROLL + LDX BLITOFFE+1 + JSR CBLITROLL + LDX BLITOFFE+2 + JSR CBLITROLL + LDX BLITOFFE+3 + JSR CBLITROLL + LDX BLITOFFE+4 + JSR CBLITROLL + LDX BLITOFFE+5 + JSR CBLITROLL + LDX BLITOFFE+6 + JSR CBLITROLL + LDX BLITOFFO+0 + JSR CBLITROLL + LDX BLITOFFO+1 + JSR CBLITROLL + LDX BLITOFFO+2 + JSR CBLITROLL + LDX BLITOFFO+3 + JSR CBLITROLL + LDX BLITOFFO+4 + JSR CBLITROLL + LDX BLITOFFO+5 + JSR CBLITROLL + LDX BLITOFFO+6 + JMP CBLITROLL + +* Construct the shift tables +MAKESHIFT + LDX #0 +:SH01 + TXA + AND #3 +:SH0101 + STA SH0101,X +:SH0123 + ASL + ASL + STA SH0123,X +:SH0145 + ASL + ASL + ORA #$80 + STA SH0145,X +:SH0156 + ASL + ORA #$80 + STA SH0156,X +:SH0157 + ASL + ASL + PHP + LSR + PLP + ROR + STA SH0157,X +:SH45 + TXA + LSR + LSR + LSR + LSR + AND #3 +:SH4501 + ORA #$80 + STA SH4501,X +:SH4523 + ASL + ASL + ORA #$80 + STA SH4523,X +:SH4545 + ASL + ASL + ORA #$80 + STA SH4545,X +:SH4556 + ASL + ORA #$80 + STA SH4556,X +:SH4557 + ASL + ASL + PHP + LSR + PLP + ROR + STA SH4557,X +:NEXT + INX + BNE :SH01 + RTS + +* Template for decimation. Offsets in comments +DCMTPL + LDA (PTEX),Y ; 0 + STA BLITROLL,X ; 2 + STA BLITROLL+29,X ; 5 + LDA (PBUMP),Y ; 8 + TAY ; 10 + ; 11 + +* Unroll the decimation code +MAKEDCM + LDX #0 ; Line counter + LDA #DCMROLL + STA PDST+1 +:ONESET +; Save address to the index + JSR :STIDX + LDY #11 ; Copy the template +:COPYSET LDA DCMTPL,Y + STA (PDST),Y + DEY + BPL :COPYSET + LDY #3 + JSR :STBLIT + LDY #6 + JSR :STBLIT + LDA #11 + JSR ADVPDST +:MORE + ; Loop until all lines done + CPX #NLINES + BCC :ONESET + JSR :STIDX ; Last addr to index + JMP STRTS ; Finish with an RTS for cleanliness +:STBLIT ; Store current blit addr + LDA BLITIDXL,X + STA (PDST),Y + INY + LDA BLITIDXH,X + STA (PDST),Y + INX ; Next line + RTS +:STIDX + TXA + LSR ; One entry per two lines + TAY + LDA PDST + STA DCMIDXL,Y + LDA PDST+1 + STA DCMIDXH,Y + RTS + +STRTS LDA #$60 ; Store an RTS at PDST + LDY #0 + STA (PDST),Y + RTS +ADVPDST ; Add A to PDST + CLC + ADC PDST + STA PDST + BCC :RTS + INC PDST+1 +:RTS RTS + +* Clear all the memory we're going to fill +CLRMEM + LDX #$10 + LDA #$BE + JMP CLRSCR2 + +* Clear the screens +CLRSCR + LDX #>SCREEN + DO DBLBUF + LDA #>SCREEN+$4000 ; both hi-res screens + ELSE + LDA #>SCREEN+$2000 ; one hi-res screen + FIN +CLRSCR2 + STA :LIMIT+1 + LDY #0 + STY PDST + TYA +:OUTER STX PDST+1 +:INNER STA (PDST),Y + INY + BNE :INNER + INX +:LIMIT CPX #>SCREEN+$2000 + BNE :OUTER + RTS + +* Make a simple texture with alternating colors. +* Input: Y = tex num +* A, X: color numbers, 0-3 +SIMPLETEX + STA :LD1+1 + TXA + ASL + ASL + ASL + ASL + STA :LD2+1 + LDA TEXADRL,Y + STA PDST + LDA TEXADRH,Y + STA PDST+1 + LDX #>TEXSIZE + LDY #0 + STY :LIM+1 +:OUTER +:LD1 LDA #0 +:LD2 ORA #0 +:LUP STA (PDST),Y + INY +:LIM CPY #0 + BNE :LUP + INC PDST+1 + DEX + BMI :DONE + BNE :OUTER + LDA #BUMPS + STA PDST+1 + LDA #0 + STA :RATIOL + LDA #1 + STA :RATIOH +* Goal is to make ratio = 63 divided by targetSize. +* The calculation is cool & weird, but I verified +* in Python that the logic actually works. You +* start with hi=1, lo=0. To calculate the next +* step, add hi to low and take the sum mod the next +* target size. To use the ratio, increment by hi +* and lo. Whenever the low byte goes beyond the +* target size, add an extra to hi. +:ONEPASS + LDA LINECT ; Init A with the lo byte = target size + LSR ; ...div 2 + LDX #0 ; Hi byte always starts at zero + LDY #0 ; Location to store at +:BUMPLUP + CLC ; increment lo byte by ratio + ADC :RATIOL + CMP LINECT ; if we wrap around, need extra hi-byte bump + BCC :NOBM + SEC + SBC LINECT + INX +:NOBM PHA ; save lo byte + TXA ; now work on hi byte + CLC + ADC :RATIOH + TAX + STA (PDST),Y ; store to the table + TAY ; next loc to store + CPX #63 ; check for end of column + PLA ; get lo byte back + BCC :BUMPLUP ; loop until whole column is done + LDA #64 + JSR ADVPDST ; advance dst to next column +:NEXT + DEC LINECT ; all columns complete? + BEQ :DONE + LDA :RATIOL ; next ratio calculation (see explanation above) + CLC + ADC :RATIOH +:MODLUP CMP LINECT + BCC :NOMOD + INC :RATIOH + SEC + SBC LINECT + BNE :MODLUP ; this must indeed be a loop +:NOMOD STA :RATIOL + JMP :ONEPASS ; next column +:DONE RTS +:RATIOL DFB 0 +:RATIOH DFB 0 + +* Decimate a column of the texture +* Input: Y - texture number +* TXCOLNUM - src column num in the texture +* PIXNUM - dst pixel num in the blit roll +* LINECT - height to render, in dbl lines +* The output will be vertically centered. +DCMCOL + ; if height is zero, render nothing + LDA LINECT + BNE :NOTZERO + RTS +:NOTZERO + ; determine mip level in X reg + LDX #0 + LDA LINECT + STA :ADJHT + LDA TXCOLNUM + STA :ADJCOL + LDA #32 +:MIPLUP CMP LINECT + BCC :GOTMIP + INX + ASL :ADJHT + LSR :ADJCOL + LSR + CMP #2 + BCS :MIPLUP +:GOTMIP + DO DEBUG + LDA #"t" + JSR COUT + TYA + JSR PRBYTE + LDA #" " + JSR COUT + + LDA #"h" + JSR COUT + LDA LINECT + JSR PRBYTE + LDA #" " + JSR COUT + + LDA #"m" + JSR COUT + TXA + JSR PRBYTE + LDA #" " + JSR COUT + FIN + + ; calc addr of tex + LDA TEXADRL,Y + CLC + ADC MIPOFFL,X + STA PDST + LDA TEXADRH,Y + ADC MIPOFFH,X + STA PDST+1 + + DO DEBUG + LDA #"a" + JSR COUT + LDA PDST+1 + JSR PRBYTE + LDA PDST + JSR PRBYTE + LDA #" " + JSR COUT + FIN + +:CALCOFF ; calc offset within tex + LDA #0 + STA PTEX+1 + LDA :ADJCOL +:SHIFT ASL + ROL PTEX+1 + INX ; Note: destroys mip level + CPX #6 + BNE :SHIFT + + DO DEBUG + PHA + LDA #"x" + JSR COUT + LDA :ADJCOL + JSR PRBYTE + LDA #" " + JSR COUT + + LDA #"o" + JSR COUT + LDA PTEX+1 + JSR PRBYTE + PLA + PHA + JSR PRBYTE + LDA #" " + JSR COUT + PLA + FIN + + CLC + ADC PDST + STA PTEX + LDA PTEX+1 + ADC PDST+1 + STA PTEX+1 +; calculate bump table ptr + LDX :ADJHT + JSR CALCBUMP + ; figure first line in decim unroll + LDA #63 + SEC + SBC LINECT ; height 63 is first in decim tbl + LSR + TAX + LDA DCMIDXL,X + STA :CALL+1 + LDA DCMIDXH,X + STA :CALL+2 + ; figure last line of decim unroll + TXA + CLC + ADC LINECT + TAX + LDA DCMIDXL,X + STA PTMP + LDA DCMIDXH,X + STA PTMP+1 +; determine blit offset for writing + LDY PIXNUM + LDX BLITOFFE,Y + ; store RTS so decim returns @ right moment + LDY #0 + LDA (PTMP),Y ; save existing byte + PHA + LDA :RTS + STA (PTMP),Y + + DO DEBUG + PHX + PHY + JSR RDKEY + PHA + JSR CROUT + PLA + PLY + PLX + CMP #$9B + BNE :NOTESC + BRK +:NOTESC NOP + FIN + +:CALL JSR DCMROLL +; fix RTS to what it was before + LDY #0 + PLA + STA (PTMP),Y + + DO DEBUG + LDY BYTENUM ; to see results early + STA SETAUXZP + JSR BLITROLL + STA CLRAUXZP + FIN + +:RTS RTS +:ADJHT DFB 0 +:ADJCOL DFB 0 + +* Calc pointer into the bump table +* Input: X - height to render in dbl lines +CALCBUMP + STX :SUB+1 + LDA #0 + STA PBUMP+1 + LDA #63 ; bump 63 is actually first + SEC +:SUB SBC #0 + BPL :NOTNEG + LDA #0 +:NOTNEG + + DO DEBUG + PHA + LDA #"b" + JSR COUT + PLA + PHA + JSR PRBYTE + LDA #" " + JSR COUT + PLA + FIN + + LDX #6 +:LUP ASL + ROL PBUMP+1 + DEX + BNE :LUP + CLC + ADC #BUMPS + STA PBUMP+1 + + DO DEBUG + LDA #"p" + JSR COUT + LDA PBUMP+1 + JSR PRBYTE + LDA PBUMP + JSR PRBYTE + LDA #" " + JSR COUT + FIN + + RTS + +* Build table of screen line pointers +* on aux zero-page +MAKELINES + LDA #0 + STA LINECT + LDA #TOPLINE + STA PLINE+1 +:LUP + LDA LINECT + ASL + TAX + LDA PLINE + LDY PLINE+1 + STA SETAUXZP + STA 0,X + STY 1,X + STA CLRAUXZP + JSR NEXTLINE + INC LINECT + LDA LINECT + CMP #NLINES + BNE :LUP + RTS + +* Set screen lines to current back buf +SETBKBUF +; calculate screen start + LDA BACKBUF + ASL + ASL + ASL + ASL + ASL + CLC + ADC #$20 + STA SETAUXZP + STA $FF + LDX #0 +:LUP LDA 1,X + AND #$1F + ORA $FF + STA 1,X + INX + INX + BNE :LUP + STA CLRAUXZP + RTS + +* Load file, len-prefixed name in A/X (hi/lo), to addr on stack +* (push hi byte first, then push lo byte) +BLOAD + STX :MLICMD+1 ; filename lo + STA :MLICMD+2 ; filename hi + LDA #PRODOSBUF + STA :MLICMD+4 + LDA #$C8 ; open + LDX #3 + JSR :DOMLI + LDA :MLICMD+5 ; get handle and put it in place + STA :MLICMD+1 + PLY ; save ret addr + PLX + PLA + STA :MLICMD+2 ; load addr lo + PLA + STA :MLICMD+3 ; load addr hi + PHX ; restore ret addr + PHY + LDA #$CA ; read + STA :MLICMD+5 ; also length (more than enough) + LDX #4 + JSR :DOMLI +:CLOSE + STZ :MLICMD+1 ; close all + LDA #$CC + LDX #1 + ; fall through +:DOMLI + STA :MLIOP + STX :MLICMD + JSR MLI +:MLIOP DFB 0 + DA :MLICMD + BCS :ERR + RTS +:ERR JSR PRBYTE + JSR PRERR + LDX #$FF + TXS + JMP MONITOR +:MLICMD DS 10 + +* Copy X pages starting at pg Y to aux mem +CPTOAUX + STA SETAUXWR + STY PDST+1 + LDY #0 + STY PDST +:LUP LDA (PDST),Y + STA (PDST),Y + INY + BNE :LUP + INC PDST+1 + DEX + BNE :LUP + STA CLRAUXWR + RTS + +* Fetch the next byte from the pre-raycasted data +* Note this routine needs to be copied to aux mem. +GETCAST + LDY #0 + STA SETAUXRD + LDA (PCAST),Y + STA CLRAUXRD + INC PCAST + BNE :DONE + INC PCAST+1 +:DONE RTS + +* Test code to see if things really work +TEST + ; clear ProDOS mem map so it lets us load + LDX #$18 + LDA #1 +:MEMLUP + STA MEMMAP-1,X + LDA #0 + DEX + BNE :MEMLUP +; load the pre-raycast data + LDA #$20 ; addr hi + PHA + LDA #0 ; addr lo + PHA + LDX #<:PRECASTNM + LDA #>:PRECASTNM + JSR BLOAD +; copy it to aux mem + LDY #$20 + LDX #$60 + JSR CPTOAUX + LDA #0 ; set ptr to it + STA PCAST + LDA #$20 + STA PCAST+1 +; copy the raycast fetcher to aux mem + LDY #>GETCAST + LDX #1 + JSR CPTOAUX +; set up everything else + JSR CLRMEM + ; load the textures + LDA #>TEX0 + PHA + LDA #:TEX0NAME + JSR BLOAD + + LDA #>TEX1 + PHA + LDA #:TEX1NAME + JSR BLOAD + + LDA #>TEX2 + PHA + LDA #:TEX2NAME + JSR BLOAD + + LDA #>TEX3 + PHA + LDA #:TEX3NAME + JSR BLOAD +; build all the unrolls and tables + JSR MAKEBLIT + JSR MAKECBLIT + JSR MAKESHIFT + JSR MAKEDCM + JSR MAKEBUMPS + JSR MAKELINES + JSR CLRSCR +; set up front and back buffers + LDA #0 + STA FRONTBUF + DO DBLBUF + LDA #1 + FIN + STA BACKBUF + + BIT CLRTEXT + BIT SETHIRES + + LDA #63 + STA LINECT + LDA #1 + STA :DIR + JSR CLRBLIT +:ONELVL + LDA #0 + STA PIXNUM + STA BYTENUM + DO DBLBUF + JSR SETBKBUF + FIN + + DO DEBUG + LDA PCAST+1 + JSR PRBYTE + LDA PCAST + JSR PRBYTE + JSR CROUT + FIN + +:ONECOL + JSR GETCAST ; first byte is height + CMP #$FF + BNE :NORESET +; found end of cast data, start over + LDA #0 + STA PCAST + LDA #$20 + STA PCAST+1 + JSR GETCAST +:NORESET + CMP #63 + BCC :HTOK + LDA #62 +:HTOK STA LINECT + JSR GETCAST ; second byte is tex num and tex col + PHA + AND #$3F + CMP #63 + BCC :COLOK + LDA #62 +:COLOK STA TXCOLNUM + PLA + LSR ; div by 64 + LSR + LSR + LSR + LSR + LSR + TAY ; Y now holds tex num + JSR DCMCOL + INC PIXNUM + LDA PIXNUM + CMP #7 + BNE :ONECOL +:FLUSH LDY BYTENUM + STA SETAUXZP + JSR BLITROLL + STA CLRAUXZP + JSR CLRBLIT + LDA #0 + STA PIXNUM + INC BYTENUM + INC BYTENUM + LDA BYTENUM + CMP #18 + BNE :ONECOL +:NEXTLVL +; flip onto the screen + DO DBLBUF + LDX BACKBUF + LDA FRONTBUF + STA BACKBUF + STX FRONTBUF + LDA PAGE1,X + FIN + ; adv past FE in cast data + JSR GETCAST + CMP #$FE + BNE :ERR + JSR GETCAST + CMP #$FE + BEQ :INCDEC +:ERR BRK +:INCDEC + LDA KBD ; stop if ESC is pressed + CMP #$9B + BEQ :DONE + JMP :ONELVL +:DONE + STA KBDSTRB ; eat the keypress + BIT SETTEXT + BIT PAGE1 + RTS +:DIR DFB 1 +:TEX0NAME DFB 21 + ASC "ASSETS/BUILDING01.BIN" +:TEX1NAME DFB 21 + ASC "ASSETS/BUILDING02.BIN" +:TEX2NAME DFB 21 + ASC "ASSETS/BUILDING03.BIN" +:TEX3NAME DFB 21 + ASC "ASSETS/BUILDING04.BIN" +:PRECASTNM + DFB 18 + ASC "ASSETS/PRECAST.BIN" + + SAV RENDER diff --git a/files/GAME/SHELL b/files/GAME/SHELL new file mode 100644 index 0000000..b5c140a Binary files /dev/null and b/files/GAME/SHELL differ diff --git a/files/GAME/SHELL.S b/files/GAME/SHELL.S new file mode 100644 index 0000000..15c085f --- /dev/null +++ b/files/GAME/SHELL.S @@ -0,0 +1,202 @@ +* Tiny shell + LST OFF + + XC ; enable 65c02 ops + +* Merlin addresses +TEXTSTART = $A +TEXTEND = $E +HIMEM = $C +OBJADDR = $E2 +OBJLEN = $E4 +ORGADDR = $E6 + +* Monitor addresses +STARTADDR = $3C +ENDADDR = $3E +DESTADDR = $42 + +* My zero-pg +MLICMD = $50 + +* I/O addresses +SETMAINZP = $C008 +SETALTZP = $C009 +READROM = $C082 +LCBANK2 = $C08B + +* Other stuff +INBUF = $200 +RESETVEC = $3F2 +USERVEC = $3F5 +CTRLYVEC = $3F8 +PRODOSBUF = $BB00 +NAMEBUF = $C00 +MLI = $BF00 +MEMMAP = $BF58 +AUXMOVE = $C311 +MERLIN = $E003 +PRNTAX = $F941 +PRHEX = $F944 +BASCALC = $FBC1 +CALCRESET = $FB6F +INPUT = $FD6A +INPUTZ = $FD6F +CROUT = $FD8E +COUT = $FDED +SETVID = $FE93 +PRERR = $FF2D +MONITOR = $FF69 +MONSCAN = $FF70 + + ORG $300 + +INSTVECS LDA #TOMON + STA USERVEC+2 + + LDA #TOMERLIN + STA RESETVEC+1 + STA CTRLYVEC+2 + + JMP CALCRESET + +* Start of resident code +RUNSHELL + ; clear ProDOS memory map + LDX #$18 + LDA #1 +:MEMLUP + STA MEMMAP-1,X + LDA #0 + DEX + BNE :MEMLUP + ; display prompt +:LUP LDA PROMPT,X + BEQ :GETNAME + JSR COUT + INX + BNE :LUP +:GETNAME + JSR INPUTZ + TXA + BEQ :MON +:LOADIT + JSR LOADPROG +:MON JMP MONITOR + +* Load program, name in INBUF +LOADPROG +; translate filename + LDY #0 + STY MLICMD+1 ; filename lo byte + LDA #>NAMEBUF + STA MLICMD+2 ; ...and hi byte +:LUP + LDA INBUF,Y + INY + STA NAMEBUF,Y + CMP #$8D + BNE :LUP +:NAMEDONE + DEY + STY NAMEBUF + LDA #$C4 ; get info + LDX #$A + JSR DOMLI + LDA MLICMD+6 ; addr hi + PHA + LDA MLICMD+5 ; addr lo + PHA + LDA #PRODOSBUF + STA MLICMD+4 + LDA #$C8 ; open + LDX #3 + JSR DOMLI + LDA MLICMD+5 ; handle + STA MLICMD+1 + PLA ; addr lo + STA MLICMD+2 + PLA ; addr hi + STA MLICMD+3 + LDA #$CA ; read + STA MLICMD+5 ; also length (more than enough) + INX + JSR DOMLI + ; fall through +CLOSE + STZ MLICMD+1 ; close all + LDA #$CC + LDX #1 + ; fall through +DOMLI + STA MLIOP + STX MLICMD + JSR MLI +MLIOP DFB 0 + DA MLICMD + BCS :NO +:OK + RTS +:NO + LDA MLIOP + CMP #$CC + BEQ :OK + ; fall through +ERR + JSR PRHEX + JSR CLOSE + JSR PRERR + JMP RUNSHELL + +COPYUPPR LDX #$98 + LDY #$C0 + ; fall through + +* Copy page X-Y to page A +* Carry: clear = aux2main, set = main2aux +UNITYCOPY TXA +COPY STX STARTADDR+1 + STY ENDADDR+1 + STA DESTADDR+1 + STZ STARTADDR + STZ ENDADDR + STZ DESTADDR + JMP AUXMOVE + +TOMON + SEC + JSR COPYUPPR + STA SETMAINZP + BIT READROM + JSR INSTVECS + LDX #$FF + TXS + JSR SETVID + JMP RUNSHELL + +TOMERLIN + JSR CLOSE + CLC + JSR COPYUPPR + STA SETALTZP + LDX #$FF + TXS + LDA LCBANK2 + JMP MERLIN + +PROMPT DFB $8D + ASC "Ld:",00 + + LST ON +END DFB 0 + ERR \$3F0 + LST OFF + + SAV SHELL diff --git a/files/MERLIN.SYSTEM b/files/MERLIN.SYSTEM new file mode 100644 index 0000000..3766eab Binary files /dev/null and b/files/MERLIN.SYSTEM differ diff --git a/files/PARMS b/files/PARMS new file mode 100644 index 0000000..a9a2905 Binary files /dev/null and b/files/PARMS differ diff --git a/files/PARMS.S b/files/PARMS.S new file mode 100644 index 0000000..3915c7f --- /dev/null +++ b/files/PARMS.S @@ -0,0 +1,93 @@ +******************************** +* * +* PARMS * +* * +* for Merlin - ProDOS version. * +* * +* Glen E. Bredon 8/11/85 * +* * +******************************** + +SAVOBJ KBD "Save the object code? (1=Y, 0=N)" + +DATA + DFB 60 ;# lines/page for PRTR + DFB 0 ;Page skip (formfeed if 0) + DFB 80 ;# printer columns + DFB $80 ;- if printer does CR at +; end of # columns + + DFB $83 ;80 col flag (DO NOT CHANGE) +;(Except V-bit which will +; cause ProDOS to be moved to +; aux memory and Merlin to +; load into main memory. +; I.e., use $C3 for this.) +*------------------------------------------------- +* Source address must be above $8A0. We use $901 +* to maintain compatibility. It can be set higher +* to protect an area of memory for any purpose: +*------------------------------------------------- +SOURCE = $901 + DA SOURCE ;Start of source + DA $AA00 ;Don't change this + DA SOURCE ;End of source + + DFB <"^" ;Editor's wild card + DFB 4 ;# of symbol columns + DFB 0 ;Search chr for "Update..." +; (Question skipped if 0) + + DFB 14,20,31 ;Default tabs + + DFB 8 ;# obj bytes/line after 1st + + DFB $45 ;High bit neg. forces infinite +; wait for key upon assembly +; errors. +;V-bit set defeats bells. +;Low nibble is Ultraterm entry +; mode (e.g., 5, $45, $85 or $C5 +; gives 32x80 interlace mode) + + DFB 0 ;Regular cursor if $40 +;Block cursor if 0 +;(Ignored if Apple card) + + DFB 0 ;Default for LSTDO opcode: +;Do off areas listed if 0 or 1, +; not if >1. +;Bit 0, if clear, causes shift +; to 40 columns on a PRTR1 cmd + + DFB 80-8 ;Column for cycle count + + DFB $EC ;Ultraterm cursor in default +; mode. (This must be changed +; if the ultraterm mode is +; changed.) + + ERR *-DATA-23 ;23 data bytes to here. + +* User file type names (change to fit your desires): + + ASC "$F1" + ASC "$F2" + ASC "$F3" + ASC "$F4" + ASC "$F5" + ASC "$F6" + ASC "$F7" + + ERR *-DATA-44 ;44 bytes in all +; no more, no less + +*---------------------------------------------- +* Be sure to change the following pathname if +* you want this on a volume with another name. +* The PARMS file must be in the MAIN DIRECTORY! +*---------------------------------------------- + + DO SAVOBJ + SAV /LL/PARMS + FIN diff --git a/files/PRODOS b/files/PRODOS new file mode 100644 index 0000000..adcbb1c Binary files /dev/null and b/files/PRODOS differ diff --git a/files/STARTUP b/files/STARTUP new file mode 100644 index 0000000..3e784fc Binary files /dev/null and b/files/STARTUP differ diff --git a/platform/empty.2mg.gz b/platform/empty.2mg.gz new file mode 100644 index 0000000..686a5de Binary files /dev/null and b/platform/empty.2mg.gz differ