package com.sforzando.j2scc; public class J2SCC { public static void convert (DataInput in, DataOutput out) throws IOException { ArrayList data = new ArrayList(); while (true) { try { data.add(in.read()); } catch (EOFException eofe) { break; } } byte[] cdata = new byte[data.size()]; for (int i = 0; i < cdata.length; i++) cdata[i] = data.get(i); Class c = MethodHandles.lookup().defineClass(cdata); in = new DataInputStream(new ByteArrayInputStream(cdata)); in.skipBytes(4); int cplen = in.readUnsignedShort() - 1; byte[][] constantPool = new byte[][cplen]; for (int i = 0; i < cplen; i++) { byte id = clazz.readByte(); if (id == 1) { int len = in.readUnsignedShort(); byte[] data1 = new byte[len]; in.readFully(data1); ByteBuffer buf = ByteBuffer.allocate(len + 3); buf.put(id); buf.putShort((short) len); buf.put(data1); constantPool[i] = buf.array(); continue; } constantPool[i] = new byte[CONSTANT_LENGTHS[id]]; byte[] intermediate = new byte[CONSTANT_LENGTHS[id] - 1]; in.readFully(intermediate); System.arraycopy(intermediate, 0, constantPool[i], 1, intermediate.length); constantPool[i][0] = id; } clazz.skipBytes(6); int ilen = clazz.readUnsignedShort(); clazz.skipBytes(ilen * 2); int flen = clazz.readUnsignedShort(); for (int i = 0; i < flen; i++) { in.skipBytes(6); int len = in.readUnsignedShort(); for (int j = 0; j < len; j++) { in.skipBytes(2); int ln = in.readInt(); in.skipBytes(ln); } } int flen = clazz.readUnsignedShort(); HashMap methods = new HashMap(); for (int i = 0; i < flen; i++) { in.skipBytes(2); String name = getString(constantPool[in.readUnsignedShort()]); String desc = getString(constantPool[in.readUnsignedShort()]); int len = in.readUnsignedShort(); for (int j = 0; j < len; j++) { String name = getString(constantPool[in.readUnsignedShort()]); int ln = in.readInt(); if (!name.equals("Code")) { in.skipBytes(ln); break; } byte[] data = new byte[ln]; in.readFully(data); methods.put(name + ":" + desc, data); } } HashMap includes = new HashMap(); String title = " "; DestinationType dest = DestinationType.JAPAN; int version = 0; int hardware = 0; int ramSize = 0; for (Annotation a : c.getAnnotations()) { if (a instanceof IncludeGraphics) includes.put(getGraphics((IncludeGraphics) a)); else if (a instanceof IncludePalettes) includes.put(getPalettes((IncludePalettes) a)); else if (a instanceof IncludeBinary) includes.put(getBinary((IncludeBinary) a)); else if (a instanceof IncludeAudio) includes.put(getAudio((IncludeAudio) a)); else if (a instanceof CartridgeTitle) { if (!title.equals(" ")) throw new IllegalStateException("multiple defined cartridge titles"); String ititle = ((CartridgeTitle) a).title; if (ititle.length() > 21) throw new IllegalArgumentException("cartridge title may be at most 21 characters"); title = ititle + " ".substring(ititle.length()); } else if (a instanceof CartridgeDestination) dest = ((CartridgeDestination) a).destination; else if (a instanceof CartridgeVersion) version = ((CartridgeVersion) a).version; else if (a instanceof CartridgeHardware) { hardware = toInt((CartridgeHardware) a); ramSize = ((CartridgeHardware) a).ramSize; } else if (a instanceof CartridgeMetadata) { if (!title.equals(" ")) throw new IllegalStateException("multiple defined cartridge titles"); String ititle = ((CartridgeMetadata) a).title; if (ititle.length() > 21) throw new IllegalArgumentException("cartridge title may be at most 21 characters"); title = ititle + " ".substring(ititle.length()); dest = ((CartridgeMetadata) a).destination; version = ((CartridgeMetadata) a).version; hardware = toInt((CartridgeMetadata) a); ramSize = ((CartridgeMetadata) a).ramSize; } } HashMap vectors = new HashMap(); HashMap routines = new HashMap(); HashMap> includeRefs = new HashMap>(); HashMap> includeTypes = new HashMap>(); HashMap> routineRefs = new HashMap>(); HashMap> routineTypes = new HashMap>(); for (Method m : c.getDeclaredMethods()) { if (m.getAnnotation(J2SCCVisible.class) == null) continue; Vector v = m.getAnnotation(Vector.class); if (v != null) { if (vectors.containsKey(v)) throw new IllegalStateException("multiple defined vectors match: " + v.toString()); vectors.put(v, m.getName()); } routines.put(m.getName(), compile(methods.get(toNDString(m)), c, includeRefs, includeTypes, routineRefs, routineTypes)); } int includesLen = 0; for (String s : includes.keySet()) includesLen += includes.get(s).length; int routinesLen = 0; for (String s : routines.keySet()) routinesLen += routines.get(s).length; if (includesLen + routinesLen > 0x7F0000) throw new IllegalStateException("rom overflow"); HashMap includesOff = new HashMap(); int currentOff = 0x810000 + routinesLen; for (String s : includes.keySet()) { includesOff.put(s, currentOff); currentOff += includes.get(s).length; } HashMap routinesOff = new HashMap(); currentOff = 0x810000; for (String s : routines.keySet()) { routinesOff.put(s, currentOff); currentOff += routines.get(s).length; } for (String s : routines.keySet()) { HashMap irefs = includeRefs.get(s); HashMap itypes = includeTypes.get(s); byte[] code = routines.get(s); for (Integer i : irefs.keySet()) { Integer ref = includesOff.get(irefs.get(i)); if (ref == null) throw new IllegalStateException("unknown reference: " + irefs.get(i)); if (itypes.get(i).intValue() == 0) { code[i] = toByteUnsigned(ref.intValue() & 0xFF); code[i + 1] = toByteUnsigned((ref.intValue() & 0xFF00) >> 8); code[i + 2] = toByteUnsigned((ref.intValue() & 0x00FF0000) >> 16); } else if (itypes.get(i).intValue() == 1) { code[i] = toByteUnsigned(ref.intValue() & 0xFF); code[i + 5] = toByteUnsigned((ref.intValue() & 0xFF00) >> 8); code[i + 10] = toByteUnsigned((ref.intValue() & 0x00FF0000) >> 16); } } HashMap rrefs = routineRefs.get(s); HashMap rtypes = routineTypes.get(s); for (Integer i : rrefs.keySet()) { Integer ref = routinesOff.get(rrefs.get(i)); if (ref == null) throw new IllegalStateException("unknown reference: " + rrefs.get(i)); if (rtypes.get(i).intValue() == 0) { code[i] = toByteUnsigned(ref.intValue() & 0xFF); code[i + 1] = toByteUnsigned((ref.intValue() & 0xFF00) >> 8); code[i + 2] = toByteUnsigned((ref.intValue() & 0x00FF0000) >> 16); } else if (rtypes.get(i).intValue() == 1) { code[i] = toByteUnsigned(ref.intValue() & 0xFF); code[i + 5] = toByteUnsigned((ref.intValue() & 0xFF00) >> 8); code[i + 10] = toByteUnsigned((ref.intValue() & 0x00FF0000) >> 16); } } routines.put(s, code); } HashMap vecoffs = new HashMap(); HashMap vecoffs2 = new HashMap(); int vecoffs2c = 0; for (Vector v : vectors.keySet()) { vecoffs.put(v, routinesOff.get(vectors.get(v))); vecoffs2.put(v, vecoffs2c); vecoffs2c += 4; } ByteBuffer buf = ByteBuffer.allocate(0x800000); buf.put(new byte[0x8000]); for (Vector v : vecoffs.keySet()) { int ref = vecoffs.get(v); buf.put(0x5C); buf.put(toByteUnsigned(ref & 0xFF)); buf.put(toByteUnsigned((ref.intValue() & 0xFF00) >> 8)); buf.put(toByteUnsigned((ref.intValue() & 0x00FF0000) >> 16)); } buf.put(new byte[0x7FB0 - (vectors.size() * 4)]); buf.put("JC".getBytes()); buf.put(" ".getBytes()); buf.put(new byte[10]); buf.put(title.getBytes()); buf.put((byte) 0x21); buf.put(toByteUnsigned(hardware)); buf.put((byte) 23); buf.put(toByteUnsigned(ramSize)); buf.put(toByteUnsigned(dest.getCode())); buf.put(toByteUnsigned(version)); buf.putShort((short) 0); buf.putShort(Short.reverseBytes(toShortUnsigned(0xFFFF))); buf.put(new byte[4]); buf.putShort(Short.reverseBytes(toShortUnsigned(vecoffs2.get(Vector.of(VectorType.COPROCESSOR, EmulationMode.NATIVE))))); buf.putShort(Short.reverseBytes(toShortUnsigned(vecoffs2.get(Vector.of(VectorType.BREAK, EmulationMode.NATIVE))))); buf.put(new byte[2]); buf.putShort(Short.reverseBytes(toShortUnsigned(vecoffs2.get(Vector.of(VectorType.NON_MASKABLE_INTERRUPT, EmulationMode.NATIVE))))); buf.put(new byte[2]); buf.putShort(Short.reverseBytes(toShortUnsigned(vecoffs2.get(Vector.of(VectorType.INTERRUPT_REQUEST, EmulationMode.NATIVE))))); buf.put(new byte[4]); buf.putShort(Short.reverseBytes(toShortUnsigned(vecoffs2.get(Vector.of(VectorType.COPROCESSOR, EmulationMode.EMULATED))))); buf.put(new byte[4]); buf.putShort(Short.reverseBytes(toShortUnsigned(vecoffs2.get(Vector.of(VectorType.NON_MASKABLE_INTERRUPT, EmulationMode.EMULATED))))); buf.putShort(Short.reverseBytes(toShortUnsigned(vecoffs2.get(Vector.of(VectorType.RESET, EmulationMode.EMULATED))))); buf.putShort(Short.reverseBytes(toShortUnsigned(vecoffs2.get(Vector.of(VectorType.INTERRUPT_REQUEST, EmulationMode.EMULATED))))); for (String s : includesOff.keySet()) buf.put(includesOff.get(s).intValue() - 0x800000, includes.get(s)); for (String s : routinesOff.keySet()) buf.put(routinesOff.get(s).intValue() - 0x800000, routines.get(s)); buf.put(new byte[0x7F0000 - (includesLen + routinesLen)]); int checksum = 0; for (int i = 0; i < 0x800000; i++) checksum = (checksum + buf.get(i)) & 0xFFFF; buf.putShort(0xFFDC, Short.reverseBytes(toShortUnsigned(checksum ^ 0xFFFF))); buf.putShort(0xFFDE, Short.reverseBytes(toShortUnsigned(checksum))); out.writeFully(buf.array()); } private final byte[] translateReservedMethod (String method, Class c, Object... args) { if (method.equals("NMITIMEN") || method.equals("setGenericProperties")) { if (args[0] instanceof RegisterConvertible rc) return rc.store(0x4200); int i = 0; if (args[0] instanceof Boolean b && b) i |= 0b10000000; if (args[1] instanceof Boolean b && b) i |= 0b00100000; if (args[2] instanceof Boolean b && b) i |= 0b00010000; if (args[3] instanceof Boolean b && b) i |= 0b00000001; if (i == 0) return new byte[] { 0x9C, 0x00, 0x42 }; return new byte[] { 0xA9, toByteUnsigned(i), 0x8D, 0x00, 0x42 }; } else if (method.equals("WRMPY") || method.equals("multiply")) { byte[] store1; if (args[0] instanceof RegisterConvertible rc) store1 = rc.store(0x4202); else throw new IllegalArgumentException(method + " requires argument 1 to be integer-like"); byte[] store2; if (args[1] instanceof RegisterConvertible rc) store1 = rc.store(0x4203); else throw new IllegalArgumentException(method + " requires argument 2 to be integer-like"); byte[] wait = new byte[2]; Arrays.fill(wait, 0xEA); byte[] ret = new byte[store1.length + store2.length + wait.length]; System.arraycopy(store1, 0, ret, 0, store1.length); System.arraycopy(store2, 0, ret, store1.length, store2.length); System.arraycopy(wait, 0, ret, store1.length + store2.length, wait.length); return ret; } else if (method.equals("WRDIV") || method.equals("divide")) { byte[] store1; if (args[0] instanceof RegisterConvertibleLong rc) store1 = rc.store(0x4204); else throw new IllegalArgumentException(method + " requires argument 1 to be integer-like"); byte[] store2; if (args[1] instanceof RegisterConvertible rc) store1 = rc.store(0x4206); else throw new IllegalArgumentException(method + " requires argument 2 to be integer-like"); byte[] wait = new byte[6]; Arrays.fill(wait, 0xEA); byte[] ret = new byte[store1.length + store2.length + wait.length]; System.arraycopy(store1, 0, ret, 0, store1.length); System.arraycopy(store2, 0, ret, store1.length, store2.length); System.arraycopy(wait, 0, ret, store1.length + store2.length, wait.length); return ret; } else if (method.equals("HTIME") || method.equals("setPixelTimer")) { byte[] store; if (args[0] instanceof RegisterConvertibleLong rc) store = rc.store(0x4207); else throw new IllegalArgumentException(method + " requires argument to be integer-like"); return store; } else if (method.equals("VTIME") || method.equals("setScanlineTimer")) { byte[] store; if (args[0] instanceof RegisterConvertibleLong rc) store = rc.store(0x4209); else throw new IllegalArgumentException(method + " requires argument to be integer-like"); return store; } else if (method.equals("INIDISP") || method.equals("setVisualProperties")) { if (args[0] instanceof RegisterConvertible rc) return rc.store(0x2100); int i = 0; if (args[0] instanceof Boolean b && b) i |= 0b10000000; if (args[1] instanceof Integer j) i |= (j.intValue() & 0x0F); if (i == 0) return new byte[] { 0x9C, 0x00, 0x21 }; return new byte[] { 0xA9, toByteUnsigned(i), 0x8D, 0x00, 0x21 }; } else if (method.equals("OBJSEL") || method.equals("setSpriteProperties")) { if (args[0] instanceof RegisterConvertible rc) return rc.store(0x2101); int i = 0; int size = 0; int address1 = 0; int address2 = 0; if (args[0] instanceof Integer j && (Integer.bitCount(j.intValue()) == Integer.bitCount(j.intValue() & 0x3C) == 1)) size |= j.intValue(); if (args[1] instanceof Integer j && (Integer.bitCount(j.intValue()) == Integer.bitCount(j.intValue() & 0x3C) == 1)) size |= j.intValue(); i |= OBJECT_SIZES[size]; if (args[2] instanceof Integer j) address1 = j; if (args[3] instanceof Integer j) address2 = j; int off = address2 - address1; if (address1 != (address1 & 0x6000)) throw new IllegalArgumentException("first address must select a valid 16 KiB section of VRam"); if (off != (off & 0xFF00)) throw new IllegalArgumentException("second address must be a multiple of 256 bytes after the first"); int off2 = (off >> 8) - 1; if (off2 < 0) throw new IllegalArgumentException("second address must be after the first"); if (off2 != (off2 & 0x03)) throw new IllegalArgumentException("second address must be 0x1000 or less after the first"); i |= (off2 << 3) | (address1 >> 13); if (i == 0) return new byte[] { 0x9C, 0x01, 0x21 }; return new byte[] { 0xA9, toByteUnsigned(i), 0x8D, 0x01, 0x21 }; } else if (method.equals("BGMODE") || method.equals("setBackgroundProperties")) { if (args[0] instanceof RegisterConvertible rc) return rc.store(0x2105); int i = 0; if (args[0] instanceof Integer j) i |= (j.intValue() & 0x07); if (args[1] instanceof Boolean b && b) i |= 0x00001000; if (args[2] instanceof Boolean b && b) i |= 0x00010000; if (args[3] instanceof Boolean b && b) i |= 0x00100000; if (args[4] instanceof Boolean b && b) i |= 0x01000000; if (args[5] instanceof Boolean b && b) i |= 0x10000000; if (i == 0) return new byte[] { 0x9C, 0x05, 0x21 }; return new byte[] { 0xA9, toByteUnsigned(i), 0x8D, 0x05, 0x21 }; } else if (method.equals("MOSAIC") || method.equals("setMosaicProperties")) { if (args[0] instanceof RegisterConvertible rc) return rc.store(0x2106); int i = 0; if (args[0] instanceof Integer j) i |= (j.intValue() & 0x0F); if (args[1] instanceof Boolean b && b) i |= 0x00010000; if (args[2] instanceof Boolean b && b) i |= 0x00100000; if (args[3] instanceof Boolean b && b) i |= 0x01000000; if (args[4] instanceof Boolean b && b) i |= 0x10000000; if (i == 0) return new byte[] { 0x9C, 0x06, 0x21 }; return new byte[] { 0xA9, toByteUnsigned(i), 0x8D, 0x06, 0x21 }; } else if (method.equals("BGSC") || method.equals("setBackgroundTilemapProperties")) { if (args[0] instanceof RegisterConvertible && args[1] instanceof RegisterConvertible && args[2] instanceof RegisterConvertible && args[3] instanceof RegisterConvertible) { byte[] store1 = ((RegisterConvertible) args[0]).store(0x2107); byte[] store2 = ((RegisterConvertible) args[1]).store(0x2108); byte[] store3 = ((RegisterConvertible) args[2]).store(0x2109); byte[] store4 = ((RegisterConvertible) args[3]).store(0x210A); byte[] data = new byte[store1.length + store2.length + store3.length + store4.length]; System.arraycopy(store1, 0, data, 0, store1.length); System.arraycopy(store2, 0, data, store1.length, store2.length); System.arraycopy(store3, 0, data, store1.length + store2.length, store3.length); System.arraycopy(store4, 0, data, store1.length + store2.length + store3.length, store4.length); return data; } int bgcount = args.length / 3; byte[][] bgarray = new byte[][bgcount]; for (int k = 0; k < bgcount; k++) { int i = 0; if (args[0] instanceof Integer j) i |= (j.intValue() & 0x3F) << 2; if (args[1] instanceof Integer j && ((j.intValue() - 1) & ~1) == 0) i |= (j.intValue() & 1); if (args[2] instanceof Integer j && ((j.intValue() - 1) & ~1) == 0) i |= (j.intValue() & 1) << 1; if (i == 0) bgarray[k] = new byte[] { 0x9C, 0x07 + k, 0x42 }; else bgarray[k] = new byte[] { 0xA9, toByteUnsigned(i), 0x8D, 0x07 + k, 0x42 }; } ByteBuffer buf = ByteBuffer.allocate(bgarray[0].length + (bgcount > 1 ? bgarray[1].length) + (bgcount > 2 ? bgarray[2].length) + (bgcount > 3 ? bgarray[3].length)); for (byte[] b : bgarray) buf.put(b); return b.array(); } else if (method.equals("BGNBA") || method.equals("setBackgroundTileAddresses")) { if (args[0] instanceof RegisterConvertible && args[1] instanceof RegisterConvertible) { byte[] store1 = ((RegisterConvertible) args[0]).store(0x210B); byte[] store2 = ((RegisterConvertible) args[1]).store(0x210C); byte[] data = new byte[store1.length + store2.length]; System.arraycopy(store1, 0, data, 0, store1.length); System.arraycopy(store2, 0, data, store1.length, store2.length); return data; } int i = 0; int j = 0; if (args[0] instanceof Integer k) i |= (k.intValue() & 0x0F); if (args[1] instanceof Integer k) i |= (k.intValue() & 0x0F) << 4; if (args[2] instanceof Integer k) j |= (k.intValue() & 0x0F); if (args[3] instanceof Integer k) j |= (k.intValue() & 0x0F) << 4; byte[] store1; if (i == 0) store1 = new byte[] { 0x9C, 0x0B, 0x21 }; else store1 = new byte[] { 0xA9, toByteUnsigned(i), 0x8D, 0x0B, 0x21 }; byte[] store2; if (i == 0) store2 = new byte[] { 0x9C, 0x0C, 0x21 }; else store2 = new byte[] { 0xA9, toByteUnsigned(i), 0x8D, 0x0C, 0x21 }; byte[] data = new byte[store1.length + store2.length]; System.arraycopy(store1, 0, data, 0, store1.length); System.arraycopy(store2, 0, data, store1.length, store2.length); return data; } else if (method.equals("BGHOFS") || method.equals("setBackgroundHorizontalOffsets")) { if (args[0] instanceof RegisterConvertible && args[1] instanceof RegisterConvertible && args[2] instanceof RegisterConvertible && args[3] instanceof RegisterConvertible) { byte[] store1 = ((RegisterConvertible) args[0]).storeTwice(0x210D); byte[] store2 = ((RegisterConvertible) args[1]).storeTwice(0x210F); byte[] store3 = ((RegisterConvertible) args[0]).storeTwice(0x2111); byte[] store4 = ((RegisterConvertible) args[1]).storeTwice(0x2113); byte[] data = new byte[store1.length + store2.length + store3.length + store4.length]; System.arraycopy(store1, 0, data, 0, store1.length); System.arraycopy(store2, 0, data, store1.length, store2.length); System.arraycopy(store3, 0, data, store1.length + store2.length, store3.length); System.arraycopy(store4, 0, data, store1.length + store2.length + store3.length, store4.length); return data; } int bgcount = args.length; byte[][] bgarray = new byte[][bgcount]; for (int k = 0; k < bgcount; k++) { int i = 0; if (args[k] instanceof Integer j) i = (j.intValue() & 0x03FF); ByteBuffer buf1 = ByteBuffer.allocate(6 + (Integer.signum(i & 0xFF) * 2) + (Integer.signum(i & 0x0300) * 2)); if ((i & 0xFF) == 0) buf1.put(new byte[] { 0x9C, 0x0D + (k * 2), 0x21 }); else buf1.put(new byte[] { 0xA9, toByteUnsigned(i), 0x8D, 0x0D + (k * 2), 0x21 }); if ((i & 0x0200) == 0) buf1.put(new byte[] { 0x9C, 0x0D + (k * 2), 0x21 }); else buf1.put(new byte[] { 0xA9, toByteUnsigned((i & 0x0200) >> 8), 0x8D, 0x0D + (k * 2), 0x21 }); } ByteBuffer buf = ByteBuffer.allocate(bgarray[0].length + (bgcount > 1 ? bgarray[1].length) + (bgcount > 2 ? bgarray[2].length) + (bgcount > 3 ? bgarray[3].length)); for (byte[] b : bgarray) buf.put(b); return b.array(); } else if (method.equals("BGVOFS") || method.equals("setBackgroundVerticalOffsets")) { if (args[0] instanceof RegisterConvertible && args[1] instanceof RegisterConvertible && args[2] instanceof RegisterConvertible && args[3] instanceof RegisterConvertible) { byte[] store1 = ((RegisterConvertible) args[0]).storeTwice(0x210E); byte[] store2 = ((RegisterConvertible) args[1]).storeTwice(0x2110); byte[] store3 = ((RegisterConvertible) args[0]).storeTwice(0x2112); byte[] store4 = ((RegisterConvertible) args[1]).storeTwice(0x2114); byte[] data = new byte[store1.length + store2.length + store3.length + store4.length]; System.arraycopy(store1, 0, data, 0, store1.length); System.arraycopy(store2, 0, data, store1.length, store2.length); System.arraycopy(store3, 0, data, store1.length + store2.length, store3.length); System.arraycopy(store4, 0, data, store1.length + store2.length + store3.length, store4.length); return data; } int bgcount = args.length; byte[][] bgarray = new byte[][bgcount]; for (int k = 0; k < bgcount; k++) { int i = 0; if (args[k] instanceof Integer j) i = (j.intValue() & 0x03FF); ByteBuffer buf1 = ByteBuffer.allocate(6 + (Integer.signum(i & 0xFF) * 2) + (Integer.signum(i & 0x0300) * 2)); if ((i & 0xFF) == 0) buf1.put(new byte[] { 0x9C, 0x0D + (k * 2), 0x21 }); else buf1.put(new byte[] { 0xA9, toByteUnsigned(i), 0x8D, 0x0E + (k * 2), 0x21 }); if ((i & 0x0200) == 0) buf1.put(new byte[] { 0x9C, 0x0D + (k * 2), 0x21 }); else buf1.put(new byte[] { 0xA9, toByteUnsigned((i & 0x0200) >> 8), 0x8D, 0x0E + (k * 2), 0x21 }); } ByteBuffer buf = ByteBuffer.allocate(bgarray[0].length + (bgcount > 1 ? bgarray[1].length) + (bgcount > 2 ? bgarray[2].length) + (bgcount > 3 ? bgarray[3].length)); for (byte[] b : bgarray) buf.put(b); return b.array(); } else if (method.equals("VMAIN") || method.equals("setVRamAddressProperties")) { if (args[0] instanceof RegisterConvertible rc) rc.store(0x2115); int i = 0; if (args[0] instanceof VRamAddressIncrementMode aim) i |= (aim == VRamAddressIncrementMode.INCREMENT_ON_LOW_WRITE ? 0 : 1); if (args[0] instanceof VRamAddressRemapping ar) i |= (VRamAddressRemapping.getCode() << 2); if (args[0] instanceof Integer j) { switch (j.intValue()) { case 1: i |= 0; break; case 32: i |= 1; break; case 128: i |= 2; break; default: throw new IllegalArgumentException("acceptable word increment values for " + method + " are 1, 32 or 128"); } } if (i == 0) return new byte[] { 0x9C, 0x15, 0x21 }; return new byte[] { 0xA9, toByteUnsigned(i), 0x8D, 0x15, 0x21 }; } else if (method.equals("M7SEL") || method.equals("setBackgroundMode7Properties")) { if (args[0] instanceof RegisterConvertible rc) return rc.store(0x211A); int i = 0; if (args[1] instanceof Boolean b && b) i |= 0x00000001; if (args[2] instanceof Boolean b && b) i |= 0x00000010; if (args[3] instanceof Mode7NonTilemapFill ntf) i |= (ntf.getCode() << 6); if (args[4] instanceof Boolean b && b) i |= 0x10000000; if (i == 0) return new byte[] { 0x9C, 0x1A, 0x21 }; return new byte[] { 0xA9, toByteUnsigned(i), 0x8D, 0x1A, 0x21 }; } else if (method.equals("M7ABCDXY") || method.equals("setBackgroundMode7Matrix")) { int[][] arr = new int[][8]; if (args[0] instanceof int[][] a) arr = a; else { if (args[0] instanceof Integer j) arr[0][0] = j.intValue(); if (args[1] instanceof Integer j) arr[0][1] = j.intValue(); if (args[2] instanceof Integer j) arr[0][2] = j.intValue(); if (args[3] instanceof Integer j) arr[0][3] = j.intValue(); if (args[4] instanceof Integer j) arr[1][0] = j.intValue(); if (args[5] instanceof Integer j) arr[1][1] = j.intValue(); if (args[6] instanceof Integer j) arr[1][2] = j.intValue(); if (args[7] instanceof Integer j) arr[1][3] = j.intValue(); } for (int i = 4; i < arr.length; i++) arr[i] = arr[i] & 0x1FFF; byte[][] buf1 = new byte[][8]; int buf1len = 0; for (int i = 0; i < arr.length; i++) { if ((arr[i] & 0xFF) == 0) { buf1[i] = new byte[] { 0x9C, M7_MATRIX_ADDRESSES[i], 0x21 }; buf1len += 3; } else { buf1[i] = new byte[] { 0xA9, toByteUnsigned(arr[i]), 0x8D, M7_MATRIX_ADDRESSES[i], 0x21 }; buf1len += 5; } if ((arr[i] & 0xFF00) == 0) { buf1[i] = new byte[] { 0x9C, M7_MATRIX_ADDRESSES[i], 0x21 }; buf1len += 3; } else { buf1[i] = new byte[] { 0xA9, toByteUnsigned(arr[i] >> 8), 0x8D, M7_MATRIX_ADDRESSES[i], 0x21 }; buf1len += 5; } } ByteBuffer buf = ByteBuffer.allocate(buf1len); for (byte[] b : buf1) buf.put(b); return buf.array(); }