├── .DS_Store ├── .gitattributes ├── .gitignore ├── .vscode ├── launch.json └── settings.json ├── 5x8_lcd_hd44780u_a02.ttf ├── DylSCII.bin ├── README.md ├── courbd.ttf ├── resources ├── .DS_Store ├── 5x8_lcd_hd44780u_a02.ttf ├── DylSCII.bin └── courbd.ttf ├── screenshots ├── screenshot0.png ├── screenshot1.png ├── screenshot2.png └── screenshot3.png └── src ├── .DS_Store ├── ACIA.java ├── AddressMode.java ├── Bus.java ├── CPU.java ├── DisassemblyOutput.java ├── DisplayPanel.java ├── EaterEmulator.java ├── GPU.java ├── Instruction.java ├── LCD.java ├── OpCode.java ├── OptionsData.java ├── OptionsPane.java ├── Point.java ├── RAM.java ├── ROM.java ├── ROMLoader.java ├── SerialInterface.java └── VIA.java /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DylanSpeiser/Java-6502-Emulator/e6412f510576dd1e5da74d00c44376e5367c22e3/.DS_Store -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /bin/ 2 | /charImg/ 3 | .DS_Store 4 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "java", 9 | "name": "Launch LCD", 10 | "request": "launch", 11 | "mainClass": "LCD", 12 | "projectName": "Java-6502-Emulator_cdf032ed" 13 | }, 14 | { 15 | "type": "java", 16 | "name": "Launch GPU", 17 | "request": "launch", 18 | "mainClass": "GPU", 19 | "projectName": "Java-6502-Emulator_cdf032ed" 20 | }, 21 | { 22 | "type": "java", 23 | "name": "Launch EaterEmulator", 24 | "request": "launch", 25 | "mainClass": "EaterEmulator", 26 | "projectName": "Java-6502-Emulator_cdf032ed", 27 | "args": "-verbose" 28 | }, 29 | { 30 | "type": "java", 31 | "name": "Launch OptionsPane", 32 | "request": "launch", 33 | "mainClass": "OptionsPane", 34 | "projectName": "Java-6502-Emulator_cdf032ed" 35 | }, 36 | { 37 | "type": "java", 38 | "name": "Launch Current File", 39 | "request": "launch", 40 | "mainClass": "${file}" 41 | }, 42 | { 43 | "type": "java", 44 | "name": "Launch EaterEmulator", 45 | "request": "launch", 46 | "mainClass": "EaterEmulator", 47 | "projectName": "Java-6502-Emulator" 48 | }, 49 | { 50 | "type": "java", 51 | "name": "Launch LCD", 52 | "request": "launch", 53 | "mainClass": "LCD", 54 | "projectName": "Java-6502-Emulator" 55 | } 56 | ] 57 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "java.project.sourcePaths": [ 3 | "src", 4 | "resources" 5 | ] 6 | } -------------------------------------------------------------------------------- /5x8_lcd_hd44780u_a02.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DylanSpeiser/Java-6502-Emulator/e6412f510576dd1e5da74d00c44376e5367c22e3/5x8_lcd_hd44780u_a02.ttf -------------------------------------------------------------------------------- /DylSCII.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DylanSpeiser/Java-6502-Emulator/e6412f510576dd1e5da74d00c44376e5367c22e3/DylSCII.bin -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Java 6502 Emulator 2 | 3 | ## Update: v2.11: Wozmon Works + Disassembler! 4 | @lythd has once again added some great new features! For changes, see: https://github.com/DylanSpeiser/Java-6502-Emulator/pull/19#issue-2850599707 5 | 6 | ## Update: v2.10: Realistic Keyboard, Serial Emulation, and more command line args! 7 | What an update! Thanks to lythd, the emulator now supports a realistic PS/2-like keyboard mode that can be enabled in the options pane. They've also added a serial emulator that can be turned on in a separate window. It's super cool! 8 | 9 | Note that Wozmon is not running correctly through this serial interface yet, but we are working on the issue and will update when it's fixed. 10 | 11 | We've also added some new command line arguments: 12 | - `-windowWidth ` and `windowHeight ` do what they say on the tin, and should be a welcome change for those of you with differently sized monitors. 13 | - `-f ` will pre-load the ROM with the file specified. I finally got around to adding this one, and I hope you find it useful. 14 | 15 | I'd also like to take this opportunity to note that this repo is showing its age. The code is an absolute mess since I wrote it back in high school, and I'd like to take some time to completely rewrite it from the ground up using actual OOP instead of the mess of magic numbers and duplicated function calls that builds the Swing UI.
16 | It will be a big undertaking, but seeing how popular this repo is getting I'm sure everyone would appreciate it. 17 | 18 | (still) Coming soon!: 19 | - More bug fixes, Wozmon 20 | - Complete code refactor 21 | - Other 65C02 instructions 22 | 23 | ## Update: v2.9: ACIA and Command line options! 24 | Thanks to Steve Rubin, the emulator now supports ACIA communication through the command line! Like the VIA, the ACIA's address can be adjusted in the options menu. 25 | 26 | Speaking of the command line, the first command line option has been added: `-verbose`. Use it to enable debug messages in the command line, which are now disabled by default. 27 | 28 | That's it for this update. Remember to save new config files! 29 | 30 | Coming soon: 31 | - ROM preloading via a command line argument 32 | - Implement all 65C02 instructions 33 | - More bug fixes 34 | - Code cleanup 35 | 36 | ## Update: v2.8: Bigger is better! 37 | Added support for: 38 | - 20x4 LCDs 39 | - Reading LCD DDRAM Address (cursor position) 40 | - Writing LCD DDRAM Address 41 | 42 | Remember to save new config files! 43 | 44 | ## Update: v2.5: Keyboards are _SO_ hot right now. 45 | Look at that another update so quickly! This update has a bunch of bug fixes like actually being able to save preference files and more accurate clock speed calculations. It also comes with a very simple keyboard interface, detailed below: 46 | 47 | - The "Keyboard mode" button, activated with the mouse, toggles the keyboard between controlling the emulator and sending keystrokes to the CPU. 48 | - When in typing mode, pressing a key on your computer's keyboard will write the **ASCII CODE** of that key to the memory location specified in the options menu (default is $3fff). 49 | - Each key will also trigger **ONE (1)** interrupt to CA1. 50 | 51 | I have chosen to implement the keyboard in this way for simplicity. Maybe one day I will implement a more realistic emulation of a PS/2 interface like the one from Ben's videos but this works pretty well for now. I was able to make a very simple shell/text editor using it. 52 | 53 | Enjoy, and see you soon! 54 | -Dylan 55 | 56 | Note: when upgrading to a new version, your saved preferences from a previous version will not work anymore. Delete them and start anew. 57 | 58 | ## Update: v2.1 is here! 59 | Whoa! Almost a year after the last update it's back and better than ever! The emulator now includes a GPU (with Ben's bitmap and a custom character mode) and SO MUCH customizability! 60 | You can now hide and show the LCD and GPU windows with the buttons at the top right! 61 | You can now customize the address ranges of the VIA and GPU! 62 | The GPU's resolution and array size is _whatever you want it to be_! 63 | The fonts actually work now! 64 | The file choosers aren't the old Java ones! 65 | And **COLORS!!** 66 | 67 | Store the configs in our fancy new .pref files! 68 | These configuration settings are stored in your AppData or Application Support folder on Windows/Mac respectively. It generates a defaults.pref file on startup every time if you want to go back to the default settings. (options.pref is loaded on startup.) 69 | 70 | Enjoy the new features! 71 | -Dylan 72 | 73 | ## Overview 74 | This is a project I started because I wanted a place to write and test code for my Ben Eater 6502 kit. After seeing some other emulators written in C++ ~~(by sane people)~~, I tried downloading them but had trouble building them. So, I figured I would just write my own. It was a fun process, and was greatly helped by [OneLoneCoder's NES Emulator Tutorial](https://github.com/OneLoneCoder/olcNES). The LCD simulator was 100% me, and I'm proud of it. 75 | 76 | UNIMPLEMENTED FEATURES: 77 | - DECIMAL MODE 78 | - LCD Character Memory (sorry, it's a character-level simulation) 79 | - Many features on the 65C22 80 | 81 | Some might ask, why write an emulator in Java? And I would respond: "Because no one else would." Sure, Java is terribly slow (more than 1000x slower than the original!), and the fact that Java's ```byte```s and ```short```s are a pain to work with because they're signed, but it's the language I'm best in so I don't care ;) 82 | 83 | Feel free to fork it, improve it, whatever, just link back to here. Enjoy! 84 | 85 | **The font isn't mine!** 86 | 87 | ## How to Run: 88 | If you download the JAR file from the releases page, you should be able to double-click it and run it. If, for some reason, that doesn't work, you can run it from the command line with: 89 | ``` 90 | java -jar 91 | ``` 92 | 93 | ## Controls 94 | - C - Toggle Clock 95 | - Space - Pulse Clock 96 | - H/J - Decrement/Increment RAM Page 97 | - K/L - Decrement/Increment ROM Page 98 | - R - Reset 99 | - S - Toggle Slow Clock 100 | - I - Trigger Interrupt (the CA1 pin on the VIA) 101 | 102 | You can load ```.bin``` files into RAM or ROM using the File Pickers in the top right. It should be fully compatible with any binary compiled for the 6502 kit, except if it uses any unimplemented features. I might get to these sometime in the future. If I do, the repo will be updated. 103 | 104 | ## Screenshots 105 | ![Screenshot 0](screenshots/screenshot0.png?raw=true) 106 | ![Screenshot 1](screenshots/screenshot1.png?raw=true) 107 | ![Screenshot 2](screenshots/screenshot2.png?raw=true) 108 | ![Screenshot 3](screenshots/screenshot3.png?raw=true) 109 | -------------------------------------------------------------------------------- /courbd.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DylanSpeiser/Java-6502-Emulator/e6412f510576dd1e5da74d00c44376e5367c22e3/courbd.ttf -------------------------------------------------------------------------------- /resources/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DylanSpeiser/Java-6502-Emulator/e6412f510576dd1e5da74d00c44376e5367c22e3/resources/.DS_Store -------------------------------------------------------------------------------- /resources/5x8_lcd_hd44780u_a02.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DylanSpeiser/Java-6502-Emulator/e6412f510576dd1e5da74d00c44376e5367c22e3/resources/5x8_lcd_hd44780u_a02.ttf -------------------------------------------------------------------------------- /resources/DylSCII.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DylanSpeiser/Java-6502-Emulator/e6412f510576dd1e5da74d00c44376e5367c22e3/resources/DylSCII.bin -------------------------------------------------------------------------------- /resources/courbd.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DylanSpeiser/Java-6502-Emulator/e6412f510576dd1e5da74d00c44376e5367c22e3/resources/courbd.ttf -------------------------------------------------------------------------------- /screenshots/screenshot0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DylanSpeiser/Java-6502-Emulator/e6412f510576dd1e5da74d00c44376e5367c22e3/screenshots/screenshot0.png -------------------------------------------------------------------------------- /screenshots/screenshot1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DylanSpeiser/Java-6502-Emulator/e6412f510576dd1e5da74d00c44376e5367c22e3/screenshots/screenshot1.png -------------------------------------------------------------------------------- /screenshots/screenshot2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DylanSpeiser/Java-6502-Emulator/e6412f510576dd1e5da74d00c44376e5367c22e3/screenshots/screenshot2.png -------------------------------------------------------------------------------- /screenshots/screenshot3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DylanSpeiser/Java-6502-Emulator/e6412f510576dd1e5da74d00c44376e5367c22e3/screenshots/screenshot3.png -------------------------------------------------------------------------------- /src/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DylanSpeiser/Java-6502-Emulator/e6412f510576dd1e5da74d00c44376e5367c22e3/src/.DS_Store -------------------------------------------------------------------------------- /src/ACIA.java: -------------------------------------------------------------------------------- 1 | import java.util.Scanner; 2 | 3 | public class ACIA { 4 | private byte DATA = 0; 5 | private byte COMMAND = 0; 6 | private byte STATUS =0; 7 | private byte CONTROL = 0; 8 | 9 | private Scanner s; 10 | 11 | public ACIA() { 12 | s = new Scanner(System.in); 13 | 14 | } 15 | 16 | public byte getDATA() { 17 | return DATA; 18 | } 19 | 20 | public byte getCOMMAND() { 21 | return COMMAND; 22 | } 23 | 24 | public byte getSTATUS() { 25 | return STATUS; 26 | } 27 | 28 | public byte getCONTROL() { 29 | return CONTROL; 30 | } 31 | 32 | public byte read(short address) { 33 | 34 | switch (Short.toUnsignedInt(address) - Bus.ACIA_ADDRESS) { 35 | case 0x00: 36 | return EaterEmulator.serial.getKey(); // get serial key 37 | case 0x01: 38 | STATUS = (byte) (EaterEmulator.serial.hasKey() ? 0x08 : 0x00); 39 | return STATUS; // Read Status Register 40 | case 0x02: 41 | return COMMAND; // Read Command Register 42 | 43 | case 0x03: 44 | return CONTROL; // Read Control Register 45 | default: 46 | System.err.printf("Attempted to read from invalid ACIA register: %04x\n", address); 47 | return 0; 48 | 49 | } 50 | } 51 | 52 | public void write(short address, byte data) { 53 | switch (Short.toUnsignedInt(address) - Bus.ACIA_ADDRESS) { 54 | case 0x00: 55 | EaterEmulator.serial.receiveKey(data); 56 | 57 | case 0x01: // Programmed Reset 58 | // Clear bits 4 through 0 in the Command Register and bit 2 in the Status 59 | // Register 60 | //COMMAND &= 0b11100000; 61 | //STATUS &= 0b11111011; //i dont remember this but just doing what was written 62 | //EaterEmulator.serial.reset(); 63 | //okay this is triggering in some random places and im kinda confused why so ill just leave it empty like how it was before 64 | case 0x02: 65 | COMMAND = data; 66 | break; // Write Command Register 67 | case 0x03: 68 | CONTROL = data; 69 | break; // Write Control Register 70 | default: 71 | System.err.printf("Attempted to write to invalid ACIA register: %04x\n", address); 72 | 73 | } 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /src/AddressMode.java: -------------------------------------------------------------------------------- 1 | public enum AddressMode { 2 | ACC, // OPC A or OPC accumulator (implied) 3 | ABS, // OPC $LLHH absolute 4 | ABX, // OPC $LLHH,X absolute, x-indexed 5 | ABY, // OPC $LLHH,Y absolute, y-indexed 6 | IMM, // OPC #$BB immediate 7 | IMP, // OPC implied 8 | IND, // OPC ($LLHH) indirect 9 | IZX, // OPC ($LL,X) indirect zeropage, x-indexed 10 | IZY, // OPC ($LL),Y indirect zeropage, y-indexd 11 | REL, // OPC $BB relative 12 | ZPP, // OPC $LL zeropage 13 | ZPX, // OPC $LL,X zeropage, x-indexed 14 | ZPY, // OPC $LL,Y zeropage, y-indexed 15 | 16 | // WDC 65c02 ADDITIONS // 17 | 18 | ZPI, // OPC ($LL) zeropage indirect 19 | IAX, // OPC ($LLHH,X) indirect, x-indexed 20 | } 21 | -------------------------------------------------------------------------------- /src/Bus.java: -------------------------------------------------------------------------------- 1 | public class Bus { 2 | public static int VIA_ADDRESS = 0x7ff0; 3 | public static int ACIA_ADDRESS = 0x5000; 4 | 5 | public static byte read(short address) { 6 | if (Short.toUnsignedInt(address) >= 0x8000) { 7 | return EaterEmulator.rom.read((short)(address-0x8000)); 8 | } else if (Short.toUnsignedInt(address) <= VIA_ADDRESS+16 && Short.toUnsignedInt(address) >= VIA_ADDRESS) { 9 | return EaterEmulator.via.read(address); 10 | } else if (Short.toUnsignedInt(address) <= ACIA_ADDRESS+3 && Short.toUnsignedInt(address) >= ACIA_ADDRESS) { 11 | if (EaterEmulator.verbose) System.out.println("Read from address "+Integer.toHexString(Short.toUnsignedInt(address))); 12 | return EaterEmulator.acia.read(address); 13 | } else { 14 | return EaterEmulator.ram.read(address); 15 | } 16 | } 17 | 18 | public static void write(short address, byte data) { 19 | if (Short.toUnsignedInt(address) >= 0x8000) { 20 | System.err.println("Can't write to ROM! ("+Integer.toHexString(Short.toUnsignedInt(address)).toUpperCase()+")"); 21 | } else if (Short.toUnsignedInt(address) <= VIA_ADDRESS+16 && Short.toUnsignedInt(address) >= VIA_ADDRESS) { 22 | EaterEmulator.via.write(address, data); 23 | } else if (Short.toUnsignedInt(address) <= ACIA_ADDRESS+3 && Short.toUnsignedInt(address) >= ACIA_ADDRESS) { 24 | EaterEmulator.acia.write(address, data); 25 | if (EaterEmulator.verbose) System.out.println("Wrote "+ROMLoader.byteToHexString(data)+" at "+Integer.toHexString(Short.toUnsignedInt(address))); 26 | } else { 27 | EaterEmulator.ram.write(address, data); 28 | } 29 | 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/CPU.java: -------------------------------------------------------------------------------- 1 | import java.util.Arrays; 2 | 3 | public class CPU { 4 | public byte flags = 0x00; 5 | //C,Z,I,D,B,U,V,N 6 | //Carry, Zero, Interrupt Disable, Decimal, Break, Unused, Overflow, Negative 7 | 8 | public byte a = 0x00; 9 | public byte x = 0x00; 10 | public byte y = 0x00; 11 | public byte stackPointer = 0x00; 12 | public short programCounter = 0x0000; 13 | 14 | public boolean debug = false; 15 | 16 | public short addressAbsolute = 0x0000; 17 | public short addressRelative = 0x0000; 18 | public byte opcode = 0x00; 19 | public int cycles = 0; 20 | 21 | public double ClocksPerSecond = 0; 22 | public int clockDelta = 1; 23 | public int lastClocks = 0; 24 | 25 | public long startTime = 0; 26 | public long timeDelta = 1; 27 | public long lastTime = System.nanoTime(); 28 | 29 | public int additionalCycles = 0; 30 | 31 | public boolean interruptRequested = false; 32 | public boolean NMinterruptRequested = false; 33 | 34 | public boolean stopped = false, waiting = false; 35 | 36 | public Instruction[] lookup = new Instruction[0x100]; 37 | 38 | public CPU() { 39 | reset(); 40 | 41 | Arrays.fill(lookup, new Instruction(OpCode.XXX, AddressMode.IMP, 2, false)); 42 | 43 | // useful reference for instructions: https://www.masswerk.at/6502/6502_instruction_set.html 44 | 45 | //ADC 46 | lookup[0x69] = new Instruction(OpCode.ADC, AddressMode.IMM, 2, false); 47 | lookup[0x65] = new Instruction(OpCode.ADC, AddressMode.ZPP, 3, false); 48 | lookup[0x75] = new Instruction(OpCode.ADC, AddressMode.ZPX, 4, false); 49 | lookup[0x6D] = new Instruction(OpCode.ADC, AddressMode.ABS, 4, false); 50 | lookup[0x7D] = new Instruction(OpCode.ADC, AddressMode.ABX, 4, false); 51 | lookup[0x79] = new Instruction(OpCode.ADC, AddressMode.ABY, 4, false); 52 | lookup[0x61] = new Instruction(OpCode.ADC, AddressMode.IZX, 6, false); 53 | lookup[0x71] = new Instruction(OpCode.ADC, AddressMode.IZY, 5, false); 54 | lookup[0x72] = new Instruction(OpCode.ADC, AddressMode.ZPI, 5, true); 55 | 56 | lookup[0x29] = new Instruction(OpCode.AND, AddressMode.IMM, 2, false); 57 | lookup[0x25] = new Instruction(OpCode.AND, AddressMode.ZPP, 3, false); 58 | lookup[0x35] = new Instruction(OpCode.AND, AddressMode.ZPX, 4, false); 59 | lookup[0x2D] = new Instruction(OpCode.AND, AddressMode.ABS, 4, false); 60 | lookup[0x3D] = new Instruction(OpCode.AND, AddressMode.ABX, 4, false); 61 | lookup[0x39] = new Instruction(OpCode.AND, AddressMode.ABY, 4, false); 62 | lookup[0x21] = new Instruction(OpCode.AND, AddressMode.IZX, 6, false); 63 | lookup[0x31] = new Instruction(OpCode.AND, AddressMode.IZY, 5, false); 64 | lookup[0x32] = new Instruction(OpCode.AND, AddressMode.ZPI, 5, true); 65 | 66 | lookup[0x0A] = new Instruction(OpCode.ASL, AddressMode.ACC, 2, false); 67 | lookup[0x06] = new Instruction(OpCode.ASL, AddressMode.ZPP, 5, false); 68 | lookup[0x16] = new Instruction(OpCode.ASL, AddressMode.ZPX, 6, false); 69 | lookup[0x0E] = new Instruction(OpCode.ASL, AddressMode.ABS, 6, false); 70 | lookup[0x1E] = new Instruction(OpCode.ASL, AddressMode.ABX, 7, false); 71 | 72 | lookup[0x0F] = new Instruction(OpCode.BBR0, AddressMode.ZPP, 5, true); 73 | lookup[0x1F] = new Instruction(OpCode.BBR1, AddressMode.ZPP, 5, true); 74 | lookup[0x2F] = new Instruction(OpCode.BBR2, AddressMode.ZPP, 5, true); 75 | lookup[0x3F] = new Instruction(OpCode.BBR3, AddressMode.ZPP, 5, true); 76 | lookup[0x4F] = new Instruction(OpCode.BBR4, AddressMode.ZPP, 5, true); 77 | lookup[0x5F] = new Instruction(OpCode.BBR5, AddressMode.ZPP, 5, true); 78 | lookup[0x6F] = new Instruction(OpCode.BBR6, AddressMode.ZPP, 5, true); 79 | lookup[0x7F] = new Instruction(OpCode.BBR7, AddressMode.ZPP, 5, true); 80 | 81 | lookup[0x8F] = new Instruction(OpCode.BBS0, AddressMode.ZPP, 5, true); 82 | lookup[0x9F] = new Instruction(OpCode.BBS1, AddressMode.ZPP, 5, true); 83 | lookup[0xAF] = new Instruction(OpCode.BBS2, AddressMode.ZPP, 5, true); 84 | lookup[0xBF] = new Instruction(OpCode.BBS3, AddressMode.ZPP, 5, true); 85 | lookup[0xCF] = new Instruction(OpCode.BBS4, AddressMode.ZPP, 5, true); 86 | lookup[0xDF] = new Instruction(OpCode.BBS5, AddressMode.ZPP, 5, true); 87 | lookup[0xEF] = new Instruction(OpCode.BBS6, AddressMode.ZPP, 5, true); 88 | lookup[0xFF] = new Instruction(OpCode.BBS7, AddressMode.ZPP, 5, true); 89 | 90 | lookup[0x90] = new Instruction(OpCode.BCC, AddressMode.REL, 2, false); 91 | 92 | lookup[0xB0] = new Instruction(OpCode.BCS, AddressMode.REL, 2, false); 93 | 94 | lookup[0xF0] = new Instruction(OpCode.BEQ, AddressMode.REL, 2, false); 95 | 96 | lookup[0x89] = new Instruction(OpCode.BIT, AddressMode.IMM, 2, true); 97 | lookup[0x24] = new Instruction(OpCode.BIT, AddressMode.ZPP, 3, false); 98 | lookup[0x2C] = new Instruction(OpCode.BIT, AddressMode.ABS, 4, false); 99 | lookup[0x34] = new Instruction(OpCode.BIT, AddressMode.ZPX, 4, true); 100 | lookup[0x3C] = new Instruction(OpCode.BIT, AddressMode.ABX, 4, true); 101 | 102 | lookup[0x30] = new Instruction(OpCode.BMI, AddressMode.REL, 2, false); 103 | 104 | lookup[0xD0] = new Instruction(OpCode.BNE, AddressMode.REL, 2, false); 105 | 106 | lookup[0x10] = new Instruction(OpCode.BPL, AddressMode.REL, 2, false); 107 | 108 | lookup[0x80] = new Instruction(OpCode.BRA, AddressMode.REL, 2, true); 109 | 110 | lookup[0x00] = new Instruction(OpCode.BRK, AddressMode.IMP, 2, false); 111 | 112 | lookup[0x50] = new Instruction(OpCode.BVC, AddressMode.REL, 2, false); 113 | 114 | lookup[0x70] = new Instruction(OpCode.BVS, AddressMode.REL, 2, false); 115 | 116 | lookup[0x18] = new Instruction(OpCode.CLC, AddressMode.IMP, 2, false); 117 | 118 | lookup[0xD8] = new Instruction(OpCode.CLD, AddressMode.IMP, 2, false); 119 | 120 | lookup[0x58] = new Instruction(OpCode.CLI, AddressMode.IMP, 2, false); 121 | 122 | lookup[0xB8] = new Instruction(OpCode.CLV, AddressMode.IMP, 2, false); 123 | 124 | lookup[0xC9] = new Instruction(OpCode.CMP, AddressMode.IMM, 2, false); 125 | lookup[0xC5] = new Instruction(OpCode.CMP, AddressMode.ZPP, 3, false); 126 | lookup[0xD5] = new Instruction(OpCode.CMP, AddressMode.ZPX, 4, false); 127 | lookup[0xCD] = new Instruction(OpCode.CMP, AddressMode.ABS, 4, false); 128 | lookup[0xDD] = new Instruction(OpCode.CMP, AddressMode.ABX, 4, false); 129 | lookup[0xD9] = new Instruction(OpCode.CMP, AddressMode.ABY, 4, false); 130 | lookup[0xC1] = new Instruction(OpCode.CMP, AddressMode.IZX, 6, false); 131 | lookup[0xD1] = new Instruction(OpCode.CMP, AddressMode.IZY, 5, false); 132 | lookup[0xD2] = new Instruction(OpCode.CMP, AddressMode.ZPI, 5, true); 133 | 134 | lookup[0xE0] = new Instruction(OpCode.CPX, AddressMode.IMM, 2, false); 135 | lookup[0xE4] = new Instruction(OpCode.CPX, AddressMode.ZPP, 3, false); 136 | lookup[0xEC] = new Instruction(OpCode.CPX, AddressMode.ABS, 4, false); 137 | 138 | lookup[0xC0] = new Instruction(OpCode.CPY, AddressMode.IMM, 2, false); 139 | lookup[0xC4] = new Instruction(OpCode.CPY, AddressMode.ZPP, 3, false); 140 | lookup[0xCC] = new Instruction(OpCode.CPY, AddressMode.ABS, 4, false); 141 | 142 | lookup[0x3A] = new Instruction(OpCode.DEC, AddressMode.ACC, 2, true); 143 | lookup[0xC6] = new Instruction(OpCode.DEC, AddressMode.ZPP, 5, false); 144 | lookup[0xD6] = new Instruction(OpCode.DEC, AddressMode.ZPX, 6, false); 145 | lookup[0xCE] = new Instruction(OpCode.DEC, AddressMode.ABS, 6, false); 146 | lookup[0xDE] = new Instruction(OpCode.DEC, AddressMode.ABX, 7, false); 147 | 148 | lookup[0xCA] = new Instruction(OpCode.DEX, AddressMode.IMP, 2, false); 149 | 150 | lookup[0x88] = new Instruction(OpCode.DEY, AddressMode.IMP, 2, false); 151 | 152 | lookup[0x49] = new Instruction(OpCode.EOR, AddressMode.IMM, 2, false); 153 | lookup[0x45] = new Instruction(OpCode.EOR, AddressMode.ZPP, 3, false); 154 | lookup[0x55] = new Instruction(OpCode.EOR, AddressMode.ZPX, 4, false); 155 | lookup[0x4D] = new Instruction(OpCode.EOR, AddressMode.ABS, 4, false); 156 | lookup[0x5D] = new Instruction(OpCode.EOR, AddressMode.ABX, 4, false); 157 | lookup[0x59] = new Instruction(OpCode.EOR, AddressMode.ABY, 4, false); 158 | lookup[0x41] = new Instruction(OpCode.EOR, AddressMode.IZX, 6, false); 159 | lookup[0x51] = new Instruction(OpCode.EOR, AddressMode.IZY, 5, false); 160 | lookup[0x52] = new Instruction(OpCode.EOR, AddressMode.ZPI, 5, true); 161 | 162 | lookup[0x1A] = new Instruction(OpCode.INC, AddressMode.ACC, 2, true); 163 | lookup[0xE6] = new Instruction(OpCode.INC, AddressMode.ZPP, 5, false); 164 | lookup[0xF6] = new Instruction(OpCode.INC, AddressMode.ZPX, 6, false); 165 | lookup[0xEE] = new Instruction(OpCode.INC, AddressMode.ABS, 6, false); 166 | lookup[0xFE] = new Instruction(OpCode.INC, AddressMode.ABX, 7, false); 167 | 168 | lookup[0xE8] = new Instruction(OpCode.INX, AddressMode.IMP, 2, false); 169 | 170 | lookup[0xC8] = new Instruction(OpCode.INY, AddressMode.IMP, 2, false); 171 | 172 | lookup[0x4C] = new Instruction(OpCode.JMP, AddressMode.ABS, 3, false); 173 | lookup[0x6C] = new Instruction(OpCode.JMP, AddressMode.IND, 5, false); 174 | 175 | lookup[0x20] = new Instruction(OpCode.JSR, AddressMode.ABS, 6, false); 176 | 177 | lookup[0xA9] = new Instruction(OpCode.LDA, AddressMode.IMM, 2, false); 178 | lookup[0xA5] = new Instruction(OpCode.LDA, AddressMode.ZPP, 3, false); 179 | lookup[0xB5] = new Instruction(OpCode.LDA, AddressMode.ZPX, 4, false); 180 | lookup[0xAD] = new Instruction(OpCode.LDA, AddressMode.ABS, 4, false); 181 | lookup[0xBD] = new Instruction(OpCode.LDA, AddressMode.ABX, 4, false); 182 | lookup[0xB9] = new Instruction(OpCode.LDA, AddressMode.ABY, 4, false); 183 | lookup[0xA1] = new Instruction(OpCode.LDA, AddressMode.IZX, 6, false); 184 | lookup[0xB1] = new Instruction(OpCode.LDA, AddressMode.IZY, 5, false); 185 | lookup[0xB2] = new Instruction(OpCode.LDA, AddressMode.ZPI, 5, true); 186 | 187 | lookup[0xA2] = new Instruction(OpCode.LDX, AddressMode.IMM, 2, false); 188 | lookup[0xA6] = new Instruction(OpCode.LDX, AddressMode.ZPP, 3, false); 189 | lookup[0xB6] = new Instruction(OpCode.LDX, AddressMode.ZPY, 4, false); 190 | lookup[0xAE] = new Instruction(OpCode.LDX, AddressMode.ABS, 4, false); 191 | lookup[0xBE] = new Instruction(OpCode.LDX, AddressMode.ABY, 4, false); 192 | 193 | lookup[0xA0] = new Instruction(OpCode.LDY, AddressMode.IMM, 2, false); 194 | lookup[0xA4] = new Instruction(OpCode.LDY, AddressMode.ZPP, 3, false); 195 | lookup[0xB4] = new Instruction(OpCode.LDY, AddressMode.ZPX, 4, false); 196 | lookup[0xAC] = new Instruction(OpCode.LDY, AddressMode.ABS, 4, false); 197 | lookup[0xBC] = new Instruction(OpCode.LDY, AddressMode.ABX, 4, false); 198 | 199 | lookup[0x4A] = new Instruction(OpCode.LSR, AddressMode.ACC, 2, false); 200 | lookup[0x46] = new Instruction(OpCode.LSR, AddressMode.ZPP, 5, false); 201 | lookup[0x56] = new Instruction(OpCode.LSR, AddressMode.ZPX, 6, false); 202 | lookup[0x4E] = new Instruction(OpCode.LSR, AddressMode.ABS, 6, false); 203 | lookup[0x5E] = new Instruction(OpCode.LSR, AddressMode.ABX, 7, false); 204 | 205 | lookup[0xEA] = new Instruction(OpCode.NOP, AddressMode.IMP, 2, false); 206 | 207 | lookup[0x09] = new Instruction(OpCode.ORA, AddressMode.IMM, 2, false); 208 | lookup[0x05] = new Instruction(OpCode.ORA, AddressMode.ZPP, 3, false); 209 | lookup[0x15] = new Instruction(OpCode.ORA, AddressMode.ZPX, 4, false); 210 | lookup[0x0D] = new Instruction(OpCode.ORA, AddressMode.ABS, 4, false); 211 | lookup[0x1D] = new Instruction(OpCode.ORA, AddressMode.ABX, 4, false); 212 | lookup[0x19] = new Instruction(OpCode.ORA, AddressMode.ABY, 4, false); 213 | lookup[0x01] = new Instruction(OpCode.ORA, AddressMode.IZX, 6, false); 214 | lookup[0x11] = new Instruction(OpCode.ORA, AddressMode.IZY, 5, false); 215 | lookup[0x12] = new Instruction(OpCode.ORA, AddressMode.ZPI, 5, true); 216 | 217 | lookup[0x48] = new Instruction(OpCode.PHA, AddressMode.IMP, 3, false); 218 | 219 | lookup[0x08] = new Instruction(OpCode.PHP, AddressMode.IMP, 3, false); 220 | 221 | lookup[0xDA] = new Instruction(OpCode.PHX, AddressMode.IMP, 3, true); 222 | 223 | lookup[0x5A] = new Instruction(OpCode.PHY, AddressMode.IMP, 3, true); 224 | 225 | lookup[0x68] = new Instruction(OpCode.PLA, AddressMode.IMP, 4, false); 226 | 227 | lookup[0x28] = new Instruction(OpCode.PLP, AddressMode.IMP, 4, false); 228 | 229 | lookup[0xFA] = new Instruction(OpCode.PLX, AddressMode.IMP, 4, true); 230 | 231 | lookup[0x7A] = new Instruction(OpCode.PLY, AddressMode.IMP, 4, true); 232 | 233 | lookup[0x07] = new Instruction(OpCode.RMB0, AddressMode.ZPP, 5, true); 234 | lookup[0x17] = new Instruction(OpCode.RMB1, AddressMode.ZPP, 5, true); 235 | lookup[0x27] = new Instruction(OpCode.RMB2, AddressMode.ZPP, 5, true); 236 | lookup[0x37] = new Instruction(OpCode.RMB3, AddressMode.ZPP, 5, true); 237 | lookup[0x47] = new Instruction(OpCode.RMB4, AddressMode.ZPP, 5, true); 238 | lookup[0x57] = new Instruction(OpCode.RMB5, AddressMode.ZPP, 5, true); 239 | lookup[0x67] = new Instruction(OpCode.RMB6, AddressMode.ZPP, 5, true); 240 | lookup[0x77] = new Instruction(OpCode.RMB7, AddressMode.ZPP, 5, true); 241 | 242 | lookup[0x2A] = new Instruction(OpCode.ROL, AddressMode.ACC, 2, false); 243 | lookup[0x26] = new Instruction(OpCode.ROL, AddressMode.ZPP, 5, false); 244 | lookup[0x36] = new Instruction(OpCode.ROL, AddressMode.ZPX, 6, false); 245 | lookup[0x2E] = new Instruction(OpCode.ROL, AddressMode.ABS, 6, false); 246 | lookup[0x3E] = new Instruction(OpCode.ROL, AddressMode.ABX, 7, false); 247 | 248 | lookup[0x6A] = new Instruction(OpCode.ROR, AddressMode.ACC, 2, false); 249 | lookup[0x66] = new Instruction(OpCode.ROR, AddressMode.ZPP, 5, false); 250 | lookup[0x76] = new Instruction(OpCode.ROR, AddressMode.ZPX, 6, false); 251 | lookup[0x6E] = new Instruction(OpCode.ROR, AddressMode.ABS, 6, false); 252 | lookup[0x7E] = new Instruction(OpCode.ROR, AddressMode.ABX, 7, false); 253 | 254 | lookup[0x40] = new Instruction(OpCode.RTI, AddressMode.IMP, 6, false); 255 | 256 | lookup[0x60] = new Instruction(OpCode.RTS, AddressMode.IMP, 6, false); 257 | 258 | lookup[0xE9] = new Instruction(OpCode.SBC, AddressMode.IMM, 2, false); 259 | lookup[0xE5] = new Instruction(OpCode.SBC, AddressMode.ZPP, 3, false); 260 | lookup[0xF5] = new Instruction(OpCode.SBC, AddressMode.ZPX, 4, false); 261 | lookup[0xED] = new Instruction(OpCode.SBC, AddressMode.ABS, 4, false); 262 | lookup[0xFD] = new Instruction(OpCode.SBC, AddressMode.ABX, 4, false); 263 | lookup[0xF9] = new Instruction(OpCode.SBC, AddressMode.ABY, 4, false); 264 | lookup[0xE1] = new Instruction(OpCode.SBC, AddressMode.IZX, 6, false); 265 | lookup[0xF1] = new Instruction(OpCode.SBC, AddressMode.IZY, 5, false); 266 | lookup[0xF2] = new Instruction(OpCode.SBC, AddressMode.ZPI, 5, true); 267 | 268 | lookup[0x38] = new Instruction(OpCode.SEC, AddressMode.IMP, 2, false); 269 | 270 | lookup[0xF8] = new Instruction(OpCode.SED, AddressMode.IMP, 2, false); 271 | 272 | lookup[0x78] = new Instruction(OpCode.SEI, AddressMode.IMP, 2, false); 273 | 274 | lookup[0x87] = new Instruction(OpCode.SMB0, AddressMode.ZPP, 5, true); 275 | lookup[0x97] = new Instruction(OpCode.SMB1, AddressMode.ZPP, 5, true); 276 | lookup[0xA7] = new Instruction(OpCode.SMB2, AddressMode.ZPP, 5, true); 277 | lookup[0xB7] = new Instruction(OpCode.SMB3, AddressMode.ZPP, 5, true); 278 | lookup[0xC7] = new Instruction(OpCode.SMB4, AddressMode.ZPP, 5, true); 279 | lookup[0xD7] = new Instruction(OpCode.SMB5, AddressMode.ZPP, 5, true); 280 | lookup[0xE7] = new Instruction(OpCode.SMB6, AddressMode.ZPP, 5, true); 281 | lookup[0xF7] = new Instruction(OpCode.SMB7, AddressMode.ZPP, 5, true); 282 | 283 | lookup[0x85] = new Instruction(OpCode.STA, AddressMode.ZPP, 3, false); 284 | lookup[0x95] = new Instruction(OpCode.STA, AddressMode.ZPX, 4, false); 285 | lookup[0x8D] = new Instruction(OpCode.STA, AddressMode.ABS, 4, false); 286 | lookup[0x9D] = new Instruction(OpCode.STA, AddressMode.ABX, 5, false); 287 | lookup[0x99] = new Instruction(OpCode.STA, AddressMode.ABY, 5, false); 288 | lookup[0x81] = new Instruction(OpCode.STA, AddressMode.IZX, 6, false); 289 | lookup[0x91] = new Instruction(OpCode.STA, AddressMode.IZY, 6, false); 290 | lookup[0x92] = new Instruction(OpCode.STA, AddressMode.ZPI, 5, true); 291 | 292 | lookup[0xDB] = new Instruction(OpCode.STP, AddressMode.IMP, 3, true); 293 | 294 | lookup[0x86] = new Instruction(OpCode.STX, AddressMode.ZPP, 3, false); 295 | lookup[0x96] = new Instruction(OpCode.STX, AddressMode.ZPY, 4, false); 296 | lookup[0x8E] = new Instruction(OpCode.STX, AddressMode.ABS, 4, false); 297 | 298 | lookup[0x84] = new Instruction(OpCode.STY, AddressMode.ZPP, 3, false); 299 | lookup[0x94] = new Instruction(OpCode.STY, AddressMode.ZPX, 4, false); 300 | lookup[0x8C] = new Instruction(OpCode.STY, AddressMode.ABS, 4, false); 301 | 302 | lookup[0x64] = new Instruction(OpCode.STZ, AddressMode.ZPP, 3, true); 303 | lookup[0x74] = new Instruction(OpCode.STZ, AddressMode.ZPX, 4, true); 304 | lookup[0x9C] = new Instruction(OpCode.STZ, AddressMode.ABS, 4, true); 305 | lookup[0x9E] = new Instruction(OpCode.STZ, AddressMode.ABX, 4, true); 306 | 307 | lookup[0xAA] = new Instruction(OpCode.TAX, AddressMode.IMP, 2, false); 308 | 309 | lookup[0xA8] = new Instruction(OpCode.TAY, AddressMode.IMP, 2, false); 310 | 311 | lookup[0x14] = new Instruction(OpCode.TRB, AddressMode.ZPP, 5, false); 312 | lookup[0x1C] = new Instruction(OpCode.TRB, AddressMode.ABS, 6, false); 313 | 314 | lookup[0x04] = new Instruction(OpCode.TSB, AddressMode.ZPP, 5, false); 315 | lookup[0x0C] = new Instruction(OpCode.TSB, AddressMode.ABS, 6, false); 316 | 317 | lookup[0xBA] = new Instruction(OpCode.TSX, AddressMode.IMP, 2, false); 318 | 319 | lookup[0x8A] = new Instruction(OpCode.TXA, AddressMode.IMP, 2, false); 320 | 321 | lookup[0x9A] = new Instruction(OpCode.TXS, AddressMode.IMP, 2, false); 322 | 323 | lookup[0x98] = new Instruction(OpCode.TYA, AddressMode.IMP, 2, false); 324 | 325 | lookup[0xCB] = new Instruction(OpCode.WAI, AddressMode.IMP, 3, true); 326 | } 327 | 328 | void setFlag(char flag, boolean condition) { 329 | flag = Character.toUpperCase(flag); 330 | switch (flag) { 331 | case 'C': 332 | flags = setBit(flags,0,condition); 333 | break; 334 | case 'Z': 335 | flags = setBit(flags,1,condition); 336 | break; 337 | case 'I': 338 | flags = setBit(flags,2,condition); 339 | break; 340 | case 'D': 341 | flags = setBit(flags,3,condition); 342 | break; 343 | case 'B': 344 | flags = setBit(flags,4,condition); 345 | break; 346 | case 'U': 347 | flags = setBit(flags,5,condition); 348 | break; 349 | case 'V': 350 | flags = setBit(flags,6,condition); 351 | break; 352 | case 'N': 353 | flags = setBit(flags,7,condition); 354 | break; 355 | } 356 | } 357 | 358 | boolean getFlag(char flag) { 359 | flag = Character.toUpperCase(flag); 360 | switch (flag) { 361 | case 'N': 362 | return ((flags&0b10000000) == 0b10000000); 363 | case 'V': 364 | return ((flags&0b01000000) == 0b01000000); 365 | case 'U': 366 | return ((flags&0b00100000) == 0b00100000); 367 | case 'B': 368 | return ((flags&0b00010000) == 0b00010000); 369 | case 'D': 370 | return ((flags&0b00001000) == 0b00001000); 371 | case 'I': 372 | return ((flags&0b00000100) == 0b00000100); 373 | case 'Z': 374 | return ((flags&0b00000010) == 0b00000010); 375 | case 'C': 376 | return ((flags&0b00000001) == 0b00000001); 377 | } 378 | if (EaterEmulator.verbose) System.out.println("Something has gone wrong in getFlag!"); 379 | return false; 380 | } 381 | 382 | public static byte setBit(byte b, int bit, boolean value) { 383 | if (value) { 384 | b |= (byte)(0x00+Math.pow(2, bit)); 385 | } else { 386 | b &= (byte)(0xFF-Math.pow(2, bit)); 387 | } 388 | return b; 389 | } 390 | 391 | void clock() { 392 | if (interruptRequested || NMinterruptRequested) waiting = false; 393 | if (waiting || stopped) return; 394 | 395 | if (cycles == 0) { 396 | if (interruptRequested) 397 | irq(); 398 | else if (NMinterruptRequested) 399 | nmi(); 400 | else { 401 | additionalCycles = 0; 402 | opcode = Bus.read(programCounter); 403 | programCounter++; 404 | 405 | cycles = lookup[Byte.toUnsignedInt(opcode)].cycles; 406 | 407 | //Execute the functions corresponding to the addressing mode and opcode 408 | 409 | //this.getClass().getMethod(lookup[Byte.toUnsignedInt(opcode)].addressMode).invoke(this); 410 | //this.getClass().getMethod(lookup[Byte.toUnsignedInt(opcode)].opcode).invoke(this); 411 | 412 | Instruction currentInstruction = lookup[Byte.toUnsignedInt(opcode)]; 413 | 414 | executeAddressModeFunction(currentInstruction.addressMode); 415 | executeOpcodeFunction(currentInstruction.opcode); 416 | 417 | } 418 | 419 | if (debug) { 420 | System.out.print(Integer.toHexString(Short.toUnsignedInt(programCounter))+" "+lookup[Byte.toUnsignedInt(opcode)].opcode+" "+ROMLoader.byteToHexString(opcode)+" "); 421 | if (!(lookup[Byte.toUnsignedInt(opcode)].addressMode == AddressMode.IMP || lookup[Byte.toUnsignedInt(opcode)].addressMode == AddressMode.ACC || lookup[Byte.toUnsignedInt(opcode)].addressMode == AddressMode.REL)) { 422 | if (lookup[Byte.toUnsignedInt(opcode)].addressMode == AddressMode.IMM) { 423 | System.out.print("#$"+Integer.toHexString(Byte.toUnsignedInt(fetched))); 424 | } else if (lookup[Byte.toUnsignedInt(opcode)].addressMode == AddressMode.REL) { 425 | System.out.print("$"+Integer.toHexString(Byte.toUnsignedInt((byte)addressAbsolute))); 426 | } else { 427 | System.out.print("$"+Integer.toHexString(Short.toUnsignedInt(addressAbsolute))); 428 | } 429 | } else if (!(lookup[Byte.toUnsignedInt(opcode)].addressMode == AddressMode.IMP || lookup[Byte.toUnsignedInt(opcode)].addressMode == AddressMode.ACC)) { 430 | System.out.print("$"+Integer.toHexString(Short.toUnsignedInt(addressRelative))); 431 | } 432 | if (lookup[Byte.toUnsignedInt(opcode)].addressMode == AddressMode.ABX || lookup[Byte.toUnsignedInt(opcode)].addressMode == AddressMode.IZX || lookup[Byte.toUnsignedInt(opcode)].addressMode == AddressMode.ZPX) { 433 | System.out.print(",X"); 434 | } else if (lookup[Byte.toUnsignedInt(opcode)].addressMode == AddressMode.ABY || lookup[Byte.toUnsignedInt(opcode)].addressMode == AddressMode.IZY || lookup[Byte.toUnsignedInt(opcode)].addressMode == AddressMode.ZPY) { 435 | System.out.print(",Y"); 436 | } 437 | System.out.print(" A:"+Integer.toHexString(Byte.toUnsignedInt(a))+" X:"+Integer.toHexString(Byte.toUnsignedInt(x))+" Y:"+Integer.toHexString(Byte.toUnsignedInt(y))+" Flags:"+ROMLoader.padStringWithZeroes(Integer.toBinaryString(Byte.toUnsignedInt(flags)), 8)); 438 | if (EaterEmulator.verbose) System.out.println(); 439 | } 440 | 441 | if (DisplayPanel.breakpoints.contains(programCounter)) EaterEmulator.clockState = false; 442 | } 443 | 444 | EaterEmulator.clocks++; 445 | 446 | cycles--; 447 | 448 | if (cycles < 0) { 449 | cycles = 0; 450 | } 451 | } 452 | 453 | void executeAddressModeFunction(AddressMode addressMode) { 454 | switch (addressMode) { 455 | case ACC, IMP -> IMP(); 456 | case ABS -> ABS(); 457 | case ABX -> ABX(); 458 | case ABY -> ABY(); 459 | case IMM -> IMM(); 460 | case IND -> IND(); 461 | case IZX -> IZX(); 462 | case IZY -> IZY(); 463 | case REL -> REL(); 464 | case ZPP -> ZPP(); 465 | case ZPX -> ZPX(); 466 | case ZPY -> ZPY(); 467 | case ZPI -> ZPI(); 468 | case IAX -> IAX(); 469 | default -> { 470 | if (EaterEmulator.verbose) 471 | System.out.println("Something has gone seriously wrong! AddressMode: " + addressMode); 472 | } 473 | } 474 | } 475 | 476 | void executeOpcodeFunction(OpCode opcode) { 477 | switch (opcode) { 478 | case ADC -> ADC(); 479 | case AND -> AND(); 480 | case ASL -> ASL(); 481 | case BBR0 -> BBR0(); 482 | case BBR1 -> BBR1(); 483 | case BBR2 -> BBR2(); 484 | case BBR3 -> BBR3(); 485 | case BBR4 -> BBR4(); 486 | case BBR5 -> BBR5(); 487 | case BBR6 -> BBR6(); 488 | case BBR7 -> BBR7(); 489 | case BBS0 -> BBS0(); 490 | case BBS1 -> BBS1(); 491 | case BBS2 -> BBS2(); 492 | case BBS3 -> BBS3(); 493 | case BBS4 -> BBS4(); 494 | case BBS5 -> BBS5(); 495 | case BBS6 -> BBS6(); 496 | case BBS7 -> BBS7(); 497 | case BCC -> BCC(); 498 | case BCS -> BCS(); 499 | case BEQ -> BEQ(); 500 | case BIT -> BIT(); 501 | case BMI -> BMI(); 502 | case BNE -> BNE(); 503 | case BPL -> BPL(); 504 | case BRA -> BRA(); 505 | case BRK -> BRK(); 506 | case BVC -> BVC(); 507 | case BVS -> BVS(); 508 | case CLC -> CLC(); 509 | case CLD -> CLD(); 510 | case CLI -> CLI(); 511 | case CLV -> CLV(); 512 | case CMP -> CMP(); 513 | case CPX -> CPX(); 514 | case CPY -> CPY(); 515 | case DEC -> DEC(); 516 | case DEX -> DEX(); 517 | case DEY -> DEY(); 518 | case EOR -> EOR(); 519 | case INC -> INC(); 520 | case INX -> INX(); 521 | case INY -> INY(); 522 | case JMP -> JMP(); 523 | case JSR -> JSR(); 524 | case LDA -> LDA(); 525 | case LDX -> LDX(); 526 | case LDY -> LDY(); 527 | case LSR -> LSR(); 528 | case NOP -> NOP(); 529 | case ORA -> ORA(); 530 | case PHA -> PHA(); 531 | case PHP -> PHP(); 532 | case PHX -> PHX(); 533 | case PHY -> PHY(); 534 | case PLA -> PLA(); 535 | case PLP -> PLP(); 536 | case PLX -> PLX(); 537 | case PLY -> PLY(); 538 | case RMB0 -> RMB0(); 539 | case RMB1 -> RMB1(); 540 | case RMB2 -> RMB2(); 541 | case RMB3 -> RMB3(); 542 | case RMB4 -> RMB4(); 543 | case RMB5 -> RMB5(); 544 | case RMB6 -> RMB6(); 545 | case RMB7 -> RMB7(); 546 | case ROL -> ROL(); 547 | case ROR -> ROR(); 548 | case RTI -> RTI(); 549 | case RTS -> RTS(); 550 | case SBC -> SBC(); 551 | case SEC -> SEC(); 552 | case SED -> SED(); 553 | case SEI -> SEI(); 554 | case SMB0 -> SMB0(); 555 | case SMB1 -> SMB1(); 556 | case SMB2 -> SMB2(); 557 | case SMB3 -> SMB3(); 558 | case SMB4 -> SMB4(); 559 | case SMB5 -> SMB5(); 560 | case SMB6 -> SMB6(); 561 | case SMB7 -> SMB7(); 562 | case STA -> STA(); 563 | case STP -> STP(); 564 | case STX -> STX(); 565 | case STY -> STY(); 566 | case STZ -> STZ(); 567 | case TAX -> TAX(); 568 | case TAY -> TAY(); 569 | case TRB -> TRB(); 570 | case TSB -> TSB(); 571 | case TSX -> TSX(); 572 | case TXA -> TXA(); 573 | case TXS -> TXS(); 574 | case TYA -> TYA(); 575 | case WAI -> WAI(); 576 | case XXX -> XXX(); 577 | default -> { 578 | if (EaterEmulator.verbose) 579 | System.out.println("Something has gone seriously wrong! OpCode: " + opcode); 580 | } 581 | } 582 | } 583 | 584 | //Input Signal Handlers 585 | void reset() { 586 | stopped = false; 587 | waiting = false; 588 | 589 | EaterEmulator.clockState = false; 590 | if (EaterEmulator.serial != null) EaterEmulator.serial.reset(); 591 | 592 | a = 0; 593 | x = 0; 594 | y = 0; 595 | stackPointer = (byte)0xFD; 596 | flags = (byte)(getFlag('U') ? 0b00000100 : 0); 597 | 598 | addressAbsolute = (short)(0xFFFC); 599 | 600 | byte lo = Bus.read(addressAbsolute); 601 | byte hi = Bus.read((short)(addressAbsolute+1)); 602 | programCounter = (short)(Byte.toUnsignedInt(lo)+256*Byte.toUnsignedInt(hi)); 603 | 604 | EaterEmulator.clocks = 0; 605 | ClocksPerSecond = 0; 606 | 607 | addressRelative = 0; 608 | addressAbsolute = 0; 609 | fetched = 0; 610 | 611 | cycles = 8; 612 | 613 | startTime = System.currentTimeMillis(); 614 | 615 | opcode = Bus.read(programCounter); 616 | } 617 | 618 | void irq() { 619 | if (!getFlag('I')) { 620 | if (debug) 621 | if (EaterEmulator.verbose) System.out.println("Interrupted!"); 622 | 623 | Bus.write((short)(0x0100+Byte.toUnsignedInt(stackPointer)), (byte)(programCounter>>8)); 624 | stackPointer--; 625 | Bus.write((short)(0x0100+Byte.toUnsignedInt(stackPointer)), (byte)(programCounter)); 626 | stackPointer--; 627 | 628 | setFlag('B',false); 629 | setFlag('U',false); 630 | Bus.write((short)(0x0100+Byte.toUnsignedInt(stackPointer)), flags); 631 | stackPointer--; 632 | setFlag('I',true); 633 | 634 | addressAbsolute = (short)(0xFFFE); 635 | byte lo = Bus.read(addressAbsolute); 636 | byte hi = Bus.read((short)(addressAbsolute+1)); 637 | programCounter = (short)(Byte.toUnsignedInt(lo)+256*Byte.toUnsignedInt(hi)); 638 | 639 | cycles = 7; 640 | } 641 | interruptRequested = false; 642 | } 643 | 644 | void nmi() { 645 | Bus.write((short)(0x0100+Byte.toUnsignedInt(stackPointer)), (byte)(programCounter>>8)); 646 | stackPointer--; 647 | Bus.write((short)(0x0100+Byte.toUnsignedInt(stackPointer)), (byte)(programCounter)); 648 | stackPointer--; 649 | 650 | setFlag('B',false); 651 | setFlag('U',false); 652 | Bus.write((short)(0x0100+Byte.toUnsignedInt(stackPointer)), flags); 653 | stackPointer--; 654 | setFlag('I',true); 655 | 656 | addressAbsolute = (short)(0xFFFA); 657 | byte lo = Bus.read(addressAbsolute); 658 | byte hi = Bus.read((short)(addressAbsolute+1)); 659 | programCounter = (short)(Byte.toUnsignedInt(lo)+256*Byte.toUnsignedInt(hi)); 660 | 661 | cycles = 7; 662 | NMinterruptRequested = false; 663 | } 664 | 665 | //Data Getter 666 | byte fetched = 0x00; 667 | byte fetch() { 668 | if (!(lookup[Byte.toUnsignedInt(opcode)].addressMode == AddressMode.IMP || lookup[Byte.toUnsignedInt(opcode)].addressMode == AddressMode.ACC)) 669 | fetched = Bus.read(addressAbsolute); 670 | return fetched; 671 | } 672 | 673 | //Addressing Modes 674 | public void IMP() { 675 | fetched = a; 676 | } 677 | 678 | public void IMM() { 679 | addressAbsolute = programCounter++; 680 | } 681 | 682 | public void ZPP() { 683 | addressAbsolute = Bus.read(programCounter++); 684 | addressAbsolute &= 0x00FF; 685 | } 686 | 687 | public void ZPX() { 688 | addressAbsolute = (short)(Byte.toUnsignedInt(Bus.read(programCounter++))+Byte.toUnsignedInt(x)); 689 | addressAbsolute &= 0x00FF; 690 | } 691 | 692 | public void ZPY() { 693 | addressAbsolute = (short)(Byte.toUnsignedInt(Bus.read(programCounter++))+Byte.toUnsignedInt(y)); 694 | addressAbsolute &= 0x00FF; 695 | } 696 | 697 | public void REL() { 698 | addressRelative = Bus.read(programCounter++); 699 | if ((addressRelative & 0x80)==0x80) 700 | addressRelative |= (short) 0xFF00; 701 | } 702 | 703 | public void ABS() { 704 | byte lo = Bus.read(programCounter++); 705 | byte hi = Bus.read(programCounter++); 706 | 707 | addressAbsolute = (short)(Byte.toUnsignedInt(lo)+256*Byte.toUnsignedInt(hi)); 708 | } 709 | 710 | public void ABX() { 711 | byte lo = Bus.read(programCounter++); 712 | byte hi = Bus.read(programCounter++); 713 | 714 | addressAbsolute = (short)(Byte.toUnsignedInt(lo)+256*Byte.toUnsignedInt(hi)+Byte.toUnsignedInt(x)); 715 | 716 | if ((addressAbsolute & 0xFF00) != (hi<<8)) 717 | additionalCycles++; 718 | } 719 | 720 | public void ABY() { 721 | byte lo = Bus.read(programCounter++); 722 | byte hi = Bus.read(programCounter++); 723 | 724 | addressAbsolute = (short)(Byte.toUnsignedInt(lo)+256*Byte.toUnsignedInt(hi)+Byte.toUnsignedInt(y)); 725 | 726 | if ((addressAbsolute & 0xFF00) != (hi<<8)) 727 | additionalCycles++; 728 | } 729 | 730 | public void IND() { 731 | short lowPointer = (short)(Bus.read(programCounter++)&0xff); 732 | short highPointer = (short)(Bus.read(programCounter++)&0xff); 733 | 734 | short pointer = (short)((highPointer << 8) | lowPointer); 735 | 736 | addressAbsolute = (short)(Byte.toUnsignedInt(Bus.read((short)(pointer+1)))*256+Byte.toUnsignedInt(Bus.read(pointer))); 737 | } 738 | 739 | public void IZX() { 740 | byte t = Bus.read(programCounter++); 741 | 742 | byte lo = Bus.read((short)((t+x)&0x00FF)); 743 | byte hi = Bus.read((short)((t+x+1)&0x00FF)); 744 | 745 | addressAbsolute = (short)(Byte.toUnsignedInt(lo)+256*Byte.toUnsignedInt(hi)); 746 | } 747 | 748 | public void IZY() { 749 | byte t = Bus.read(programCounter++); 750 | 751 | byte lo = Bus.read((short)(t&0x00FF)); 752 | byte hi = Bus.read((short)((t+1)&0x00FF)); 753 | 754 | addressAbsolute = (short)(Byte.toUnsignedInt(lo)+256*Byte.toUnsignedInt(hi)+Byte.toUnsignedInt(y)); 755 | 756 | if ((addressAbsolute & 0xFF00) != (hi<<8)) 757 | additionalCycles++; 758 | } 759 | 760 | public void ZPI() { 761 | short lowPointer = (short) (Bus.read(programCounter++) & 0x00ff); 762 | 763 | addressAbsolute = (short)(Byte.toUnsignedInt(Bus.read((short)(lowPointer+1)))*256+Byte.toUnsignedInt(Bus.read(lowPointer))); 764 | } 765 | 766 | public void IAX() { 767 | byte lowPointer = Bus.read(programCounter++); 768 | byte highPointer = Bus.read(programCounter++); 769 | 770 | short pointer = (short)((highPointer << 8) | lowPointer); 771 | 772 | byte lo = Bus.read((short) (pointer+x)); 773 | byte hi = Bus.read((short) (pointer+x+1)); 774 | 775 | addressAbsolute = (short)(Byte.toUnsignedInt(lo)+256*Byte.toUnsignedInt(hi)); 776 | } 777 | 778 | //INSTRUCTIONS 779 | public void ADC() { 780 | fetch(); 781 | short temp = (short)((short)Byte.toUnsignedInt(a) + (short)Byte.toUnsignedInt(fetched) + (short)(getFlag('C') ? 1 : 0)); 782 | setFlag('C', temp > 255); 783 | setFlag('Z', (temp & 0x00FF) == 0); 784 | setFlag('N', (temp & 0x80) == 0x80); 785 | setFlag('V', (~((short)a^(short)fetched) & ((short)a^(short)temp) & 0x0080)==0x0080); 786 | a = (byte)temp; 787 | additionalCycles++; 788 | } 789 | 790 | public void AND() { 791 | fetch(); 792 | a &= fetched; 793 | setFlag('Z', a==0x00); 794 | setFlag('N', (a & 0x80)==0x80); 795 | 796 | additionalCycles++; 797 | } 798 | 799 | public void ASL() { 800 | fetch(); 801 | short temp = (short)(fetched << 1); 802 | setFlag('Z', (temp & 0x00FF)==0x00); 803 | setFlag('C', Short.toUnsignedInt((short)(temp & 0xFF00)) > 0); 804 | setFlag('N', (temp & 0x80)==0x80); 805 | 806 | if (lookup[Byte.toUnsignedInt(opcode)].addressMode == AddressMode.ACC) { 807 | a = (byte)(temp & 0x00FF); 808 | } else { 809 | Bus.write(addressAbsolute, (byte)(temp & 0x00FF)); 810 | } 811 | } 812 | 813 | public void BBR0() { BBRn(0); } 814 | public void BBR1() { BBRn(1); } 815 | public void BBR2() { BBRn(2); } 816 | public void BBR3() { BBRn(3); } 817 | public void BBR4() { BBRn(4); } 818 | public void BBR5() { BBRn(5); } 819 | public void BBR6() { BBRn(6); } 820 | public void BBR7() { BBRn(7); } 821 | 822 | private void BBRn(int n) { 823 | if ((a & (0b1<>8)); 949 | stackPointer--; 950 | Bus.write((short)(0x0100+Byte.toUnsignedInt(stackPointer)), (byte)(programCounter)); 951 | stackPointer--; 952 | 953 | setFlag('B',true); 954 | setFlag('U',true); 955 | Bus.write((short)(0x0100+Byte.toUnsignedInt(stackPointer)), flags); 956 | stackPointer--; 957 | setFlag('I',true); 958 | 959 | addressAbsolute = (short)0xFFFE; 960 | byte lo = Bus.read(addressAbsolute); 961 | byte hi = Bus.read((short)(addressAbsolute+1)); 962 | programCounter = (short)(Byte.toUnsignedInt(lo)+256*Byte.toUnsignedInt(hi)); 963 | } 964 | 965 | public void BVC() { 966 | if (!getFlag('V')) { 967 | cycles++; 968 | addressAbsolute = (short)(programCounter+addressRelative); 969 | 970 | if ((addressAbsolute&0xFF00) != (programCounter & 0xFF00)) 971 | cycles++; 972 | 973 | programCounter = addressAbsolute; 974 | } 975 | } 976 | 977 | public void BVS() { 978 | if (getFlag('V')) { 979 | cycles++; 980 | addressAbsolute = (short)(programCounter+addressRelative); 981 | 982 | if ((addressAbsolute&0xFF00) != (programCounter & 0xFF00)) 983 | cycles++; 984 | 985 | programCounter = addressAbsolute; 986 | } 987 | } 988 | 989 | public void CLC() { 990 | setFlag('C',false); 991 | } 992 | 993 | public void CLD() { 994 | setFlag('D',false); 995 | } 996 | 997 | public void CLI() { 998 | setFlag('I',false); 999 | } 1000 | 1001 | public void CLV() { 1002 | setFlag('V',false); 1003 | } 1004 | 1005 | public void CMP() { 1006 | fetch(); 1007 | short temp = (short)(Byte.toUnsignedInt(a) - Byte.toUnsignedInt(fetched)); 1008 | setFlag('C',Byte.toUnsignedInt(a) >= Byte.toUnsignedInt(fetched)); 1009 | setFlag('Z',(temp&0x00FF)==0x0000); 1010 | setFlag('N',(temp&0x0080)==0x0080); 1011 | 1012 | additionalCycles++; 1013 | } 1014 | 1015 | public void CPX() { 1016 | fetch(); 1017 | short temp = (short)(Byte.toUnsignedInt(x) - Byte.toUnsignedInt(fetched)); 1018 | setFlag('C',Byte.toUnsignedInt(x) >= Byte.toUnsignedInt(fetched)); 1019 | setFlag('Z',(temp&0x00FF)==0x0000); 1020 | setFlag('N',(temp&0x0080)==0x0080); 1021 | } 1022 | 1023 | public void CPY() { 1024 | fetch(); 1025 | short temp = (short)(Byte.toUnsignedInt(y) - Byte.toUnsignedInt(fetched)); 1026 | setFlag('C',Byte.toUnsignedInt(y) >= Byte.toUnsignedInt(fetched)); 1027 | setFlag('Z',(temp&0x00FF)==0x0000); 1028 | setFlag('N',(temp&0x0080)==0x0080); 1029 | } 1030 | 1031 | public void DEC() { 1032 | fetch(); 1033 | int temp = (Byte.toUnsignedInt(fetched)-1); 1034 | if (lookup[Byte.toUnsignedInt(opcode)].addressMode == AddressMode.ACC) { 1035 | a = (byte)(temp&0x00FF); 1036 | } else { 1037 | Bus.write(addressAbsolute, (byte)(temp&0x00FF)); 1038 | } 1039 | setFlag('Z',(temp&0x00FF)==0x0000); 1040 | setFlag('N',(temp&0x0080)==0x0080); 1041 | } 1042 | 1043 | public void DEX() { 1044 | x--; 1045 | setFlag('Z',x==0x00); 1046 | setFlag('N',(x&0x80)==0x80); 1047 | } 1048 | 1049 | public void DEY() { 1050 | y--; 1051 | setFlag('Z',y==0x00); 1052 | setFlag('N',(y&0x80)==0x80); 1053 | } 1054 | 1055 | public void EOR() { 1056 | fetch(); 1057 | a ^= fetched; 1058 | setFlag('Z', a==0x00); 1059 | setFlag('N', (a & 0x80)==0x80); 1060 | 1061 | additionalCycles++; 1062 | } 1063 | 1064 | public void INC() { 1065 | fetch(); 1066 | short temp = (short)(fetched+1); 1067 | if (lookup[Byte.toUnsignedInt(opcode)].addressMode == AddressMode.ACC) { 1068 | a = (byte)(temp&0x00FF); 1069 | } else { 1070 | Bus.write(addressAbsolute, (byte)(temp&0x00FF)); 1071 | } 1072 | setFlag('Z',(temp&0x00FF)==0x0000); 1073 | setFlag('N',(temp&0x0080)==0x0080); 1074 | } 1075 | 1076 | public void INX() { 1077 | x++; 1078 | setFlag('Z',x==0x00); 1079 | setFlag('N',(x&0x80)==0x80); 1080 | } 1081 | 1082 | public void INY() { 1083 | y++; 1084 | setFlag('Z',y==0x00); 1085 | setFlag('N',(y&0x80)==0x80); 1086 | } 1087 | 1088 | public void JMP() { 1089 | programCounter = addressAbsolute; 1090 | } 1091 | 1092 | public void JSR() { 1093 | programCounter--; 1094 | 1095 | Bus.write((short)(0x0100+Byte.toUnsignedInt(stackPointer)), (byte)((programCounter>>8)&0x00FF)); 1096 | stackPointer--; 1097 | Bus.write((short)(0x0100+Byte.toUnsignedInt(stackPointer)), (byte)(programCounter&0x00FF)); 1098 | stackPointer--; 1099 | 1100 | programCounter = addressAbsolute; 1101 | } 1102 | 1103 | public void LDA() { 1104 | fetch(); 1105 | a = fetched; 1106 | setFlag('Z',a==0x00); 1107 | setFlag('N',(a&0x80)==0x80); 1108 | additionalCycles++; 1109 | } 1110 | 1111 | public void LDX() { 1112 | fetch(); 1113 | x = fetched; 1114 | setFlag('Z',x==0x00); 1115 | setFlag('N',(x&0x80)==0x80); 1116 | additionalCycles++; 1117 | } 1118 | 1119 | public void LDY() { 1120 | fetch(); 1121 | y = fetched; 1122 | setFlag('Z',y==0x00); 1123 | setFlag('N',(y&0x80)==0x80); 1124 | additionalCycles++; 1125 | } 1126 | 1127 | public void LSR() { 1128 | fetch(); 1129 | setFlag('C',(fetched&0x0001)==0x0001); 1130 | short temp = (short)((0x00FF&fetched) >> 1); 1131 | setFlag('Z',(temp&0x00FF)==0x0000); 1132 | setFlag('N',(temp&0x0080)==0x0080); 1133 | if (lookup[Byte.toUnsignedInt(opcode)].addressMode == AddressMode.ACC) { 1134 | a = (byte)((byte)(temp)&0x00FF); 1135 | } else { 1136 | Bus.write(addressAbsolute, (byte)(temp&0x00FF)); 1137 | } 1138 | } 1139 | 1140 | public void NOP() { 1141 | additionalCycles++; 1142 | } 1143 | 1144 | public void ORA() { 1145 | fetch(); 1146 | a |= fetched; 1147 | setFlag('Z', a==0x00); 1148 | setFlag('N', (a & 0x80)==0x80); 1149 | 1150 | additionalCycles++; 1151 | } 1152 | 1153 | public void PHA() { 1154 | Bus.write((short)(0x0100+Byte.toUnsignedInt(stackPointer)), a); 1155 | stackPointer--; 1156 | } 1157 | 1158 | public void PHP() { 1159 | Bus.write((short)(0x0100+Byte.toUnsignedInt(stackPointer)), (byte)(flags|0b00110000)); 1160 | setFlag('B',false); 1161 | setFlag('U',false); 1162 | stackPointer--; 1163 | } 1164 | 1165 | public void PHX() { 1166 | Bus.write((short)(0x0100+Byte.toUnsignedInt(stackPointer)), x); 1167 | stackPointer--; 1168 | } 1169 | 1170 | public void PHY() { 1171 | Bus.write((short)(0x0100+Byte.toUnsignedInt(stackPointer)), y); 1172 | stackPointer--; 1173 | } 1174 | 1175 | public void PLA() { 1176 | stackPointer++; 1177 | a = Bus.read((short)(0x0100+Byte.toUnsignedInt(stackPointer))); 1178 | setFlag('Z', a == 0); 1179 | setFlag('N', (a & 0x80) == 0x80); 1180 | } 1181 | 1182 | public void PLP() { 1183 | stackPointer++; 1184 | flags = Bus.read((short)(0x0100+Byte.toUnsignedInt(stackPointer))); 1185 | setFlag('U', true); 1186 | } 1187 | 1188 | public void PLX() { 1189 | stackPointer++; 1190 | x = Bus.read((short)(0x0100+Byte.toUnsignedInt(stackPointer))); 1191 | setFlag('Z', x == 0); 1192 | setFlag('N', (x & 0x80) == 0x80); 1193 | } 1194 | 1195 | public void PLY() { 1196 | stackPointer++; 1197 | y = Bus.read((short)(0x0100+Byte.toUnsignedInt(stackPointer))); 1198 | setFlag('Z', y == 0); 1199 | setFlag('N', (y & 0x80) == 0x80); 1200 | } 1201 | 1202 | public void RMB0() { RMBn(0); } 1203 | public void RMB1() { RMBn(1); } 1204 | public void RMB2() { RMBn(2); } 1205 | public void RMB3() { RMBn(3); } 1206 | public void RMB4() { RMBn(4); } 1207 | public void RMB5() { RMBn(5); } 1208 | public void RMB6() { RMBn(6); } 1209 | public void RMB7() { RMBn(7); } 1210 | 1211 | private void RMBn(int n) { 1212 | fetch(); 1213 | short temp = (short)(fetched&(0xff^(0b1<>1) | (short)(getFlag('C') ? 0x0080 : 0)); 1233 | setFlag('C',(fetched&0x01) == 0x01); 1234 | setFlag('Z',(temp&0x00FF) == 0x0000); 1235 | setFlag('N',(temp&0x0080) == 0x0080); 1236 | if (lookup[Byte.toUnsignedInt(opcode)].addressMode == AddressMode.ACC) { 1237 | a = (byte)(temp&0x00FF); 1238 | } else { 1239 | Bus.write(addressAbsolute, (byte)(temp&0x00FF)); 1240 | } 1241 | } 1242 | 1243 | public void RTI() { 1244 | stackPointer++; 1245 | flags = Bus.read((short)(0x100+Byte.toUnsignedInt(stackPointer))); 1246 | flags = (byte)(flags & (getFlag('B') ? 0b11101111 : 0)); 1247 | flags = (byte)(flags & (getFlag('U') ? 0b11011111 : 0)); 1248 | 1249 | stackPointer++; 1250 | byte lo = Bus.read((short)(0x100+Byte.toUnsignedInt(stackPointer))); 1251 | stackPointer++; 1252 | byte hi = Bus.read((short)(0x100+Byte.toUnsignedInt(stackPointer))); 1253 | programCounter = (short)(Byte.toUnsignedInt(lo)+256*Byte.toUnsignedInt(hi)); 1254 | } 1255 | 1256 | public void RTS() { 1257 | stackPointer++; 1258 | byte lo = Bus.read((short)(0x100+Byte.toUnsignedInt(stackPointer))); 1259 | stackPointer++; 1260 | byte hi = Bus.read((short)(0x100+Byte.toUnsignedInt(stackPointer))); 1261 | programCounter = (short)(Byte.toUnsignedInt(lo)+256*Byte.toUnsignedInt(hi)); 1262 | 1263 | programCounter++; 1264 | } 1265 | 1266 | public void SBC() { 1267 | fetch(); 1268 | short value = (short)(((short)fetched&0xff) ^ (short)0x00FF); 1269 | short temp = (short)(((short)a&0xff) + value + (short)(getFlag('C') ? 1 : 0)); 1270 | setFlag('C', temp > 255); 1271 | setFlag('Z', (temp & 0x00FF) == 0); 1272 | setFlag('N', (temp & 0x80) == 0x80); 1273 | setFlag('V', (~((short)a^(short)fetched) & ((short)a^(short)temp) & 0x0080)==0x0080); 1274 | a = (byte)temp; 1275 | additionalCycles++; 1276 | } 1277 | 1278 | public void SEC() { 1279 | setFlag('C',true); 1280 | } 1281 | 1282 | public void SED() { 1283 | setFlag('D',true); 1284 | } 1285 | 1286 | public void SEI() { 1287 | setFlag('I',true); 1288 | } 1289 | 1290 | public void SMB0() { SMBn(0); } 1291 | public void SMB1() { SMBn(1); } 1292 | public void SMB2() { SMBn(2); } 1293 | public void SMB3() { SMBn(3); } 1294 | public void SMB4() { SMBn(4); } 1295 | public void SMB5() { SMBn(5); } 1296 | public void SMB6() { RMBn(6); } 1297 | public void SMB7() { SMBn(7); } 1298 | 1299 | private void SMBn(int n) { 1300 | fetch(); 1301 | short temp = (short)(fetched|(0b1< programCounter += 1; 34 | case AddressMode.IMM, AddressMode.ZPP, AddressMode.ZPX, AddressMode.ZPY, AddressMode.REL, 35 | AddressMode.IZX, AddressMode.IZY, AddressMode.ZPI -> programCounter += 2; 36 | case AddressMode.ABS, AddressMode.ABX, AddressMode.ABY, AddressMode.IND, AddressMode.IAX -> 37 | programCounter += 3; 38 | } 39 | } 40 | return str.toString(); 41 | } 42 | 43 | public static String disassemble(short programCounter, short numInstructions) { 44 | StringBuilder str = new StringBuilder(); 45 | for (short j = 0; j <= numInstructions; j++) { 46 | Instruction currentInstruction = EaterEmulator.cpu.lookup[Byte.toUnsignedInt(Bus.read(programCounter))]; 47 | if (!str.isEmpty()) str.append("\n"); 48 | str.append(disassemble(programCounter)); 49 | switch (currentInstruction.addressMode) { 50 | case AddressMode.IMP, AddressMode.ACC -> programCounter += 1; 51 | case AddressMode.IMM, AddressMode.ZPP, AddressMode.ZPX, AddressMode.ZPY, AddressMode.REL, 52 | AddressMode.IZX, AddressMode.IZY, AddressMode.ZPI -> programCounter += 2; 53 | case AddressMode.ABS, AddressMode.ABX, AddressMode.ABY, AddressMode.IND, AddressMode.IAX -> 54 | programCounter += 3; 55 | } 56 | } 57 | return str.toString(); 58 | } 59 | 60 | public static String disassemble(short programCounter) { 61 | Instruction currentInstruction = EaterEmulator.cpu.lookup[Byte.toUnsignedInt(Bus.read(programCounter))]; 62 | 63 | String str = toHexShortString(programCounter, 4) + ": " + currentInstruction.opcode.toString(); 64 | switch (currentInstruction.addressMode) { 65 | case ACC -> str = str + " A"; 66 | case ABS -> str = str + " $" + busHexString((short) (programCounter+2)) + busHexString((short) (programCounter+1)); 67 | case ABX -> str = str + " $" + busHexString((short) (programCounter+2)) + busHexString((short) (programCounter+1)) + ",X"; 68 | case ABY -> str = str + " $" + busHexString((short) (programCounter+2)) + busHexString((short) (programCounter+1)) + ",Y"; 69 | case IMM -> str = str + " #$" + busHexString((short) (programCounter+1)); 70 | case IMP -> {} 71 | case IND -> str = str + " ($" + busHexString((short) (programCounter+2)) + busHexString((short) (programCounter+1)) + ")"; 72 | case IZX -> str = str + " ($" + busHexString((short) (programCounter+1)) + ",X)"; 73 | case IZY -> str = str + " ($" + busHexString((short) (programCounter+1)) + "),Y"; 74 | case REL, ZPP -> str = str + " $" + busHexString((short) (programCounter+1)); 75 | case ZPX -> str = str + " $" + busHexString((short) (programCounter+1)) + ",X"; 76 | case ZPY -> str = str + " $" + busHexString((short) (programCounter+1)) + ",Y"; 77 | case ZPI -> str = str + " ($" + busHexString((short) (programCounter+1)) + ")"; 78 | } 79 | return str; 80 | } 81 | 82 | public static String busHexString(short address) { 83 | return toHexShortString(Bus.read(address), 2); 84 | } 85 | 86 | public static String toHexShortString(int i, int packNumChars) { 87 | StringBuilder str = new StringBuilder(Integer.toHexString(i)); 88 | while (str.length() < packNumChars) str.insert(0, "0"); 89 | if (str.length() > packNumChars) str = new StringBuilder(str.substring(str.length() - packNumChars)); 90 | return str.toString(); 91 | } 92 | } 93 | 94 | class DisassemblyPanel extends JPanel { 95 | 96 | private Rectangle[] textBounds = new Rectangle[0]; 97 | private short[] nextInstructionAddresses = new short[0]; 98 | 99 | private Font smallerFont; 100 | 101 | public DisassemblyPanel() { 102 | super(null); 103 | 104 | setBackground(DisplayPanel.bgColor); 105 | setPreferredSize(new Dimension((int) (EaterEmulator.windowWidth * 0.9), (int) (EaterEmulator.windowHeight * 0.9))); 106 | 107 | try { 108 | smallerFont = Font.createFont(Font.TRUETYPE_FONT,this.getClass().getClassLoader().getResourceAsStream("courbd.ttf")).deriveFont(14f); 109 | GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); 110 | ge.registerFont(smallerFont); 111 | } catch (FontFormatException | IOException e) { 112 | e.printStackTrace(); 113 | if (EaterEmulator.verbose) System.out.println("Error loading Courier Font!"); 114 | } 115 | 116 | this.setFocusable(true); 117 | this.requestFocus(); 118 | 119 | addMouseListener(new MouseAdapter() { 120 | @Override 121 | public void mouseClicked(MouseEvent e) { 122 | // if (debug) System.out.println("mouseClicked"); 123 | for (int i = 0; i < textBounds.length; i++) { 124 | if (textBounds[i] != null && textBounds[i].contains(e.getPoint())) { 125 | if (DisplayPanel.breakpoints.contains(nextInstructionAddresses[i])) DisplayPanel.breakpoints.remove((Short)nextInstructionAddresses[i]); 126 | else DisplayPanel.breakpoints.add(nextInstructionAddresses[i]); 127 | // if (debug) System.out.println("matched with textBound " + textBounds[i] + " and it now contains it: " + breakpoints.contains(nextInstructionAddresses[i])); 128 | } 129 | } 130 | repaint(); 131 | requestFocus(); 132 | } 133 | }); 134 | } 135 | 136 | public void paintComponent(Graphics g) { 137 | super.paintComponent(g); 138 | g.setColor(DisplayPanel.fgColor); 139 | 140 | //Disassembly 141 | g.setFont(smallerFont); 142 | g.drawString("Instructions",50,30); 143 | String[] text = DisassemblyOutput.disassembleUntil((short)DisassemblyOutput.StartAddress, (short)DisassemblyOutput.EndAddress).split("\n"); 144 | int x = 75, y = 50; 145 | textBounds = new Rectangle[text.length]; 146 | nextInstructionAddresses = new short[text.length]; 147 | int lineLongestWidth = 0; 148 | for (int i = 0; i < text.length; i++) { 149 | String line = text[i]; 150 | short address = (short) Integer.parseInt(line.substring(0,4), 16); 151 | if (DisplayPanel.breakpoints.contains(address)) g.setColor(Color.RED); 152 | 153 | FontMetrics fm = g.getFontMetrics(); 154 | int textWidth = fm.stringWidth(line); 155 | if (textWidth > lineLongestWidth) lineLongestWidth = textWidth; 156 | int textHeight = fm.getAscent(); 157 | textBounds[i] = new Rectangle(x, y, textWidth, textHeight); 158 | nextInstructionAddresses[i] = address; 159 | 160 | g.drawString(line, x, y += fm.getHeight()); 161 | if (y >= getHeight() - 50) { 162 | x += lineLongestWidth + 15; 163 | lineLongestWidth = 0; 164 | y = 50; 165 | } 166 | 167 | if (DisplayPanel.breakpoints.contains(address)) g.setColor(DisplayPanel.fgColor); 168 | } 169 | } 170 | } -------------------------------------------------------------------------------- /src/DisplayPanel.java: -------------------------------------------------------------------------------- 1 | import java.awt.*; 2 | import java.awt.event.*; 3 | import javax.swing.JPanel; 4 | import javax.swing.Timer; 5 | import java.io.IOException; 6 | import java.util.ArrayList; 7 | 8 | public class DisplayPanel extends JPanel implements ActionListener, KeyListener { 9 | Timer frameTimer = new javax.swing.Timer(16, this);; 10 | Timer clocksPerSecondCheckTimer = new Timer(150,this); 11 | int ramPage = 0; 12 | int romPage = 0; 13 | 14 | int rightAlignHelper = Math.max(getWidth(), 1334); 15 | 16 | public Font courierNewBold; 17 | 18 | String ramPageString = ""; 19 | String romPageString = ""; 20 | private Rectangle[] textBounds = new Rectangle[0]; 21 | private short[] nextInstructionAddresses = new short[0]; 22 | 23 | public static ArrayList breakpoints = new ArrayList<>(); 24 | 25 | public static Color bgColor = Color.blue; 26 | public static Color fgColor = Color.white; 27 | 28 | public static ArrayList keys = new ArrayList(); 29 | 30 | boolean debug = false; 31 | 32 | public DisplayPanel() { 33 | super(null); 34 | 35 | clocksPerSecondCheckTimer.start(); 36 | frameTimer.start(); 37 | setBackground(bgColor); 38 | setPreferredSize(new Dimension(1936, 966)); 39 | 40 | try { 41 | courierNewBold = Font.createFont(Font.TRUETYPE_FONT,this.getClass().getClassLoader().getResourceAsStream("courbd.ttf")).deriveFont(20f); 42 | GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); 43 | ge.registerFont(courierNewBold); 44 | } catch (FontFormatException | IOException e) { 45 | e.printStackTrace(); 46 | if (EaterEmulator.verbose) System.out.println("Error loading Courier Font!"); 47 | } 48 | 49 | romPageString = EaterEmulator.rom.ROMString.substring(romPage*960,(romPage+1)*960); 50 | ramPageString = EaterEmulator.ram.RAMString.substring(ramPage*960,(ramPage+1)*960); 51 | 52 | this.setFocusable(true); 53 | this.requestFocus(); 54 | this.addKeyListener(this); 55 | 56 | addMouseListener(new MouseAdapter() { 57 | @Override 58 | public void mouseClicked(MouseEvent e) { 59 | // if (debug) System.out.println("mouseClicked"); 60 | for (int i = 0; i < textBounds.length; i++) { 61 | if (textBounds[i] != null && textBounds[i].contains(e.getPoint())) { 62 | if (breakpoints.contains(nextInstructionAddresses[i])) breakpoints.remove((Short)nextInstructionAddresses[i]); 63 | else breakpoints.add(nextInstructionAddresses[i]); 64 | // if (debug) System.out.println("matched with textBound " + textBounds[i] + " and it now contains it: " + breakpoints.contains(nextInstructionAddresses[i])); 65 | } 66 | } 67 | } 68 | }); 69 | } 70 | 71 | public void paintComponent(Graphics g) { 72 | super.paintComponent(g); 73 | g.setColor(fgColor); 74 | //g.drawString("Render Mode: paintComponent",5,15); 75 | 76 | // g.setColor(getBackground()); 77 | // g.fillRect(0, 0, EaterEmulator.getWindows()[1].getWidth(), EaterEmulator.getWindows()[1].getHeight()); 78 | // g.setColor(Color.white); 79 | // g.drawString("Render Mode: fillRect",5,15); 80 | 81 | rightAlignHelper = Math.max(getWidth(), 1334); 82 | 83 | //Title 84 | g.setFont(new Font("Calibri Bold", 50, 50)); 85 | g.drawString("BE65(c)02 Emulator", 40, 50); 86 | 87 | //Version 88 | g.setFont(courierNewBold); 89 | g.drawString("v"+EaterEmulator.versionString, 7, 1033); 90 | 91 | //Clocks 92 | g.drawString("Clocks: "+EaterEmulator.clocks, 40, 80); 93 | g.drawString("Speed: "+EaterEmulator.cpu.ClocksPerSecond+" Hz"+(EaterEmulator.slowerClock ? " (Slow)" : ""), 40, 110); 94 | 95 | //PAGE INDICATORS 96 | g.drawString("(K) <-- "+ROMLoader.byteToHexString((byte)(romPage+0x80))+" --> (L)", rightAlignHelper-304, Math.max(getHeight()-91, 920)); 97 | g.drawString("(H) <-- "+ROMLoader.byteToHexString((byte)ramPage)+" --> (J)", rightAlignHelper-704, Math.max(getHeight()-91, 920)); 98 | 99 | //ROM 100 | g.drawString("ROM", rightAlignHelper-214, 130); 101 | drawString(g,romPageString, rightAlignHelper-379, 150); 102 | 103 | //Stack Pointer Underline 104 | if (ramPage == 1) { 105 | g.setColor(new Color(0.7f,0f,0f)); 106 | g.fillRect(rightAlignHelper-708+36*(Byte.toUnsignedInt(EaterEmulator.cpu.stackPointer)%8), 156+23*((int)Byte.toUnsignedInt(EaterEmulator.cpu.stackPointer)/8), 25, 22); 107 | g.setColor(fgColor); 108 | } 109 | 110 | //RAM 111 | g.drawString("RAM", rightAlignHelper-624, 130); 112 | drawString(g,ramPageString, rightAlignHelper-779, 150); 113 | 114 | //Disassembly 115 | if (rightAlignHelper-800 > 1000) { // only show if theres room 116 | g.drawString("Upcoming Instructions",750,130); 117 | String[] text = DisassemblyOutput.disassemble(EaterEmulator.cpu.programCounter, (short)32).split("\n"); 118 | int x = 775, y = 150; 119 | textBounds = new Rectangle[text.length]; 120 | nextInstructionAddresses = new short[text.length]; 121 | for (int i = 0; i < text.length; i++) { 122 | String line = text[i]; 123 | short address = (short) Integer.parseInt(line.substring(0,4), 16); 124 | if (breakpoints.contains(address)) g.setColor(Color.RED); 125 | 126 | FontMetrics fm = g.getFontMetrics(); 127 | int textWidth = fm.stringWidth(line); 128 | int textHeight = fm.getAscent(); 129 | textBounds[i] = new Rectangle(x, y, textWidth, textHeight); 130 | nextInstructionAddresses[i] = address; 131 | 132 | g.drawString(line, x, y += fm.getHeight()); 133 | 134 | if (breakpoints.contains(address)) g.setColor(fgColor); 135 | } 136 | } else { 137 | textBounds = new Rectangle[0]; 138 | nextInstructionAddresses = new short[0]; 139 | } 140 | 141 | //CPU 142 | g.drawString("CPU Registers:",50,140); 143 | g.drawString("A: "+ROMLoader.padStringWithZeroes(Integer.toBinaryString(Byte.toUnsignedInt(EaterEmulator.cpu.a)), 8)+" ("+ROMLoader.byteToHexString(EaterEmulator.cpu.a)+")", 35, 170); 144 | g.drawString("X: "+ROMLoader.padStringWithZeroes(Integer.toBinaryString(Byte.toUnsignedInt(EaterEmulator.cpu.x)), 8)+" ("+ROMLoader.byteToHexString(EaterEmulator.cpu.x)+")", 35, 200); 145 | g.drawString("Y: "+ROMLoader.padStringWithZeroes(Integer.toBinaryString(Byte.toUnsignedInt(EaterEmulator.cpu.y)), 8)+" ("+ROMLoader.byteToHexString(EaterEmulator.cpu.y)+")", 35, 230); 146 | g.drawString("Stack Pointer: "+ROMLoader.padStringWithZeroes(Integer.toBinaryString(Byte.toUnsignedInt(EaterEmulator.cpu.stackPointer)), 8)+" ("+ROMLoader.byteToHexString(EaterEmulator.cpu.stackPointer)+")", 35, 260); 147 | g.drawString("Program Counter: "+ROMLoader.padStringWithZeroes(Integer.toBinaryString(Short.toUnsignedInt(EaterEmulator.cpu.programCounter)), 16)+" ("+ROMLoader.padStringWithZeroes(Integer.toHexString(Short.toUnsignedInt(EaterEmulator.cpu.programCounter)).toUpperCase(),4)+")", 35, 290); 148 | g.drawString("Flags: ("+ROMLoader.byteToHexString(EaterEmulator.cpu.flags)+")", 35, 320); 149 | 150 | g.drawString("Absolute Address: "+ROMLoader.padStringWithZeroes(Integer.toBinaryString(Short.toUnsignedInt(EaterEmulator.cpu.addressAbsolute)), 16)+" ("+ROMLoader.byteToHexString((byte)(EaterEmulator.cpu.addressAbsolute/0xFF))+ROMLoader.byteToHexString((byte)EaterEmulator.cpu.addressAbsolute)+")", 35, 350); 151 | g.drawString("Relative Address: "+ROMLoader.padStringWithZeroes(Integer.toBinaryString(Short.toUnsignedInt(EaterEmulator.cpu.addressRelative)), 16)+" ("+ROMLoader.byteToHexString((byte)(EaterEmulator.cpu.addressRelative/0xFF))+ROMLoader.byteToHexString((byte)EaterEmulator.cpu.addressRelative)+")", 35, 380); 152 | g.drawString("Opcode: "+EaterEmulator.cpu.lookup[Byte.toUnsignedInt(EaterEmulator.cpu.opcode)]+" ("+ROMLoader.byteToHexString(EaterEmulator.cpu.opcode)+")", 35, 410); 153 | g.drawString("Cycles: "+EaterEmulator.cpu.cycles, 35, 440); 154 | 155 | int counter = 0; 156 | String flagsString = "NVUBDIZC"; 157 | for (char c : ROMLoader.padStringWithZeroes(Integer.toBinaryString(Byte.toUnsignedInt(EaterEmulator.cpu.flags)),8).toCharArray()) { 158 | g.setColor((c == '1') ? Color.green : Color.red); 159 | g.drawString(String.valueOf(flagsString.charAt(counter)), 120+16*counter, 320); 160 | counter++; 161 | } 162 | 163 | g.setColor(fgColor); 164 | //VIA 165 | g.drawString("VIA Registers:",50,490); 166 | g.drawString("PORT A: "+ROMLoader.padStringWithZeroes(Integer.toBinaryString(Byte.toUnsignedInt(EaterEmulator.via.PORTA)), 8)+" ("+ROMLoader.byteToHexString(EaterEmulator.via.PORTA)+")", 35, 520); 167 | g.drawString("PORT B: "+ROMLoader.padStringWithZeroes(Integer.toBinaryString(Byte.toUnsignedInt(EaterEmulator.via.PORTB)), 8)+" ("+ROMLoader.byteToHexString(EaterEmulator.via.PORTB)+")", 35, 550); 168 | g.drawString("DDR A: "+ROMLoader.padStringWithZeroes(Integer.toBinaryString(Byte.toUnsignedInt(EaterEmulator.via.DDRA)), 8)+" ("+ROMLoader.byteToHexString(EaterEmulator.via.DDRA)+")", 35, 580); 169 | g.drawString("DDR B: "+ROMLoader.padStringWithZeroes(Integer.toBinaryString(Byte.toUnsignedInt(EaterEmulator.via.DDRB)), 8)+" ("+ROMLoader.byteToHexString(EaterEmulator.via.DDRB)+")", 35, 610); 170 | g.drawString(" PCR: "+ROMLoader.padStringWithZeroes(Integer.toBinaryString(Byte.toUnsignedInt(EaterEmulator.via.PCR)), 8)+" ("+ROMLoader.byteToHexString(EaterEmulator.via.PCR)+")", 35, 640); 171 | g.drawString(" IFR: "+ROMLoader.padStringWithZeroes(Integer.toBinaryString(Byte.toUnsignedInt(EaterEmulator.via.IFR)), 8)+" ("+ROMLoader.byteToHexString(EaterEmulator.via.IFR)+")", 35, 670); 172 | g.drawString(" IER: "+ROMLoader.padStringWithZeroes(Integer.toBinaryString(Byte.toUnsignedInt(EaterEmulator.via.IER)), 8)+" ("+ROMLoader.byteToHexString(EaterEmulator.via.IER)+")", 35, 700); 173 | 174 | //ACIA 175 | g.drawString("ACIA Registers:",350,490); 176 | g.drawString("Data Register: "+ROMLoader.padStringWithZeroes(Integer.toBinaryString(Byte.toUnsignedInt(EaterEmulator.acia.getDATA())), 8)+" ("+ROMLoader.byteToHexString(EaterEmulator.acia.getDATA())+")", 325, 520); 177 | g.drawString("Command Register: "+ROMLoader.padStringWithZeroes(Integer.toBinaryString(Byte.toUnsignedInt(EaterEmulator.acia.getCOMMAND())), 8)+" ("+ROMLoader.byteToHexString(EaterEmulator.acia.getCOMMAND())+")", 325, 550); 178 | g.drawString("Status Register: "+ROMLoader.padStringWithZeroes(Integer.toBinaryString(Byte.toUnsignedInt(EaterEmulator.acia.getSTATUS())), 8)+" ("+ROMLoader.byteToHexString(EaterEmulator.acia.getSTATUS())+")", 325, 580); 179 | g.drawString("Control Register: "+ROMLoader.padStringWithZeroes(Integer.toBinaryString(Byte.toUnsignedInt(EaterEmulator.acia.getCONTROL())), 8)+" ("+ROMLoader.byteToHexString(EaterEmulator.acia.getCONTROL())+")", 325, 610); 180 | 181 | //Controls 182 | 183 | if (!EaterEmulator.keyboardMode) { 184 | g.drawString("Controls:", 50, 750); 185 | g.drawString("C - Toggle Clock", 35, 780); 186 | g.drawString("Space - Pulse Clock", 35, 810); 187 | g.drawString("R - Reset", 35, 840); 188 | g.drawString("S - Toggle Slower Clock", 35, 870); 189 | g.drawString("I - Trigger VIA CA1", 35, 900); 190 | if (nextInstructionAddresses.length > 0) g.drawString("Clicking on any instructions will add a breakpoint there", 35, 930); 191 | } else { 192 | g.drawString("Keyboard Mode Controls:", 50, 750); 193 | if (EaterEmulator.realisticKeyboard) { 194 | g.drawString("Typing a key will store it to the VIA, you can retrieve it ", 35, 780); 195 | g.drawString("from the memory location "+EaterEmulator.options.KeyboardLocationHexLabel.getText().substring(3)+" and trigger an interrupt.", 35, 810); 196 | if (nextInstructionAddresses.length > 0) g.drawString("Clicking on any instructions will add a breakpoint there", 35, 840); 197 | } else { 198 | g.drawString("Typing a key will write that key code to", 35, 780); 199 | g.drawString("the memory location "+EaterEmulator.options.KeyboardLocationHexLabel.getText().substring(3)+" and trigger an interrupt.", 35, 810); 200 | if (nextInstructionAddresses.length > 0) g.drawString("Clicking on any instructions will add a breakpoint there", 35, 840); 201 | } 202 | } 203 | } 204 | 205 | public static void drawString(Graphics g, String text, int x, int y) { 206 | for (String line : text.split("\n")) 207 | g.drawString(line, x, y += g.getFontMetrics().getHeight()); 208 | } 209 | 210 | public void resetGraphics() { 211 | bgColor = EaterEmulator.options.data.bgColor; 212 | fgColor = EaterEmulator.options.data.fgColor; 213 | setBackground(bgColor); 214 | } 215 | 216 | @Override 217 | public void actionPerformed(ActionEvent e) { 218 | if (e.getSource().equals(frameTimer)) { 219 | 220 | EaterEmulator.running = true; 221 | 222 | ramPageString = EaterEmulator.ram.RAMString.substring(ramPage*960,(ramPage+1)*960); 223 | EaterEmulator.ROMopenButton.setBounds(rightAlignHelper-150, 15, 125, 25); 224 | EaterEmulator.RAMopenButton.setBounds(rightAlignHelper-150, 45, 125, 25); 225 | EaterEmulator.ShowLCDButton.setBounds(rightAlignHelper-300, 15, 125, 25); 226 | EaterEmulator.ShowGPUButton.setBounds(rightAlignHelper-300, 45, 125, 25); 227 | EaterEmulator.ResetButton.setBounds(rightAlignHelper-450, 15, 125, 25); 228 | EaterEmulator.ShowSerialButton.setBounds(rightAlignHelper-450, 45, 125, 25); 229 | EaterEmulator.optionsButton.setBounds(rightAlignHelper-600, 15, 125, 25); 230 | EaterEmulator.keyboardButton.setBounds(rightAlignHelper-600, 45, 125, 25); 231 | this.repaint(); 232 | 233 | if (!EaterEmulator.options.isVisible() && !EaterEmulator.serial.isVisible()) 234 | this.requestFocus(); 235 | } else if (e.getSource().equals(clocksPerSecondCheckTimer)) { 236 | EaterEmulator.cpu.timeDelta = System.nanoTime()-EaterEmulator.cpu.lastTime; 237 | EaterEmulator.cpu.lastTime = System.nanoTime(); 238 | 239 | EaterEmulator.cpu.clockDelta = EaterEmulator.clocks - EaterEmulator.cpu.lastClocks; 240 | EaterEmulator.cpu.lastClocks = EaterEmulator.clocks; 241 | 242 | EaterEmulator.cpu.ClocksPerSecond = Math.round(EaterEmulator.cpu.clockDelta/((double)EaterEmulator.cpu.timeDelta/1000000000.0)); 243 | } 244 | } 245 | 246 | @Override 247 | public void keyPressed(KeyEvent arg0) { 248 | if (EaterEmulator.keyboardMode && EaterEmulator.realisticKeyboard) { 249 | keys.add(convertToPs2(arg0)); 250 | if(EaterEmulator.verbose&&debug) System.out.println("Pressed key " + arg0.getKeyChar() + " " + convertToPs2(arg0) + " : " + (arg0.getKeyCode())); 251 | EaterEmulator.via.CA1(); 252 | } 253 | } 254 | 255 | @Override 256 | public void keyReleased(KeyEvent arg0) { 257 | if (EaterEmulator.keyboardMode && EaterEmulator.realisticKeyboard) { 258 | keys.add((byte) 0xf0); 259 | keys.add(convertToPs2(arg0)); 260 | if(EaterEmulator.verbose&&debug) System.out.println("Pressed key " + arg0.getKeyChar() + " " + convertToPs2(arg0) + " : " + (arg0.getKeyCode())); 261 | EaterEmulator.via.CA1(); 262 | } 263 | } 264 | 265 | @Override 266 | public void keyTyped(KeyEvent arg0) { 267 | if (!EaterEmulator.keyboardMode) { 268 | //Control Keyboard Mode 269 | switch (arg0.getKeyChar()) { 270 | case 'l': 271 | if (romPage < 0x7f) { 272 | romPage+=1; 273 | romPageString = EaterEmulator.rom.ROMString.substring(romPage*960,(romPage+1)*960); 274 | } else { 275 | if (romPage > 0x7f) { 276 | romPage = 0x7f; 277 | romPageString = EaterEmulator.rom.ROMString.substring(romPage*960,(romPage+1)*960); 278 | } 279 | } 280 | break; 281 | case 'k': 282 | if (romPage > 0) { 283 | romPage-=1; 284 | romPageString = EaterEmulator.rom.ROMString.substring(romPage*960,(romPage+1)*960); 285 | } 286 | break; 287 | case 'j': 288 | if (ramPage < 0x7f) { 289 | ramPage+=1; 290 | ramPageString = EaterEmulator.ram.RAMString.substring(ramPage*960,(ramPage+1)*960); 291 | if (ramPage > 0x7f) { 292 | ramPage = 0x7f; 293 | ramPageString = EaterEmulator.ram.RAMString.substring(ramPage*960,(ramPage+1)*960); 294 | } 295 | } 296 | break; 297 | case 'h': 298 | if (ramPage > 0) { 299 | ramPage-=1; 300 | ramPageString = EaterEmulator.ram.RAMString.substring(ramPage*960,(ramPage+1)*960); 301 | } 302 | break; 303 | case 'r': 304 | EaterEmulator.reset(); 305 | break; 306 | case ' ': 307 | EaterEmulator.cpu.clock(); 308 | break; 309 | case 'c': 310 | EaterEmulator.clockState = !EaterEmulator.clockState; 311 | break; 312 | case 's': 313 | EaterEmulator.slowerClock = !EaterEmulator.slowerClock; 314 | break; 315 | case 'i': 316 | EaterEmulator.via.CA1(); 317 | break; 318 | } 319 | } else if (!EaterEmulator.realisticKeyboard) { //realistic keyboard uses key pressed and released so it can get the key code, so doesn't need to check here 320 | //Typing Keyboard Mode 321 | Bus.write((short)EaterEmulator.options.data.keyboardLocation, (byte)arg0.getKeyChar()); 322 | EaterEmulator.via.CA1(); 323 | } 324 | } 325 | 326 | 327 | //converts java keyCode to ps2 keyCode to be compatible 328 | //found https://techdocs.altium.com/display/FPGA/PS2+Keyboard+Scan+Codes 329 | private byte convertToPs2(KeyEvent keyEvent) { 330 | switch(keyEvent.getKeyCode()) { 331 | case KeyEvent.VK_ESCAPE: 332 | return 0x76; 333 | case KeyEvent.VK_F1: 334 | return 0x05; 335 | case KeyEvent.VK_F2: 336 | return 0x06; 337 | case KeyEvent.VK_F3: 338 | return 0x04; 339 | case KeyEvent.VK_F4: 340 | return 0x0C; 341 | case KeyEvent.VK_F5: 342 | return 0x03; 343 | case KeyEvent.VK_F6: 344 | return 0x0B; 345 | case KeyEvent.VK_F7: 346 | return (byte) 0x83; 347 | case KeyEvent.VK_F8: 348 | return 0x0A; 349 | case KeyEvent.VK_F9: 350 | return 0x01; 351 | case KeyEvent.VK_F10: 352 | return 0x09; 353 | case KeyEvent.VK_F11: 354 | return 0x78; 355 | case KeyEvent.VK_F12: 356 | return 0x07; 357 | case KeyEvent.VK_SCROLL_LOCK: 358 | return 0x7E; 359 | case KeyEvent.VK_DEAD_GRAVE: 360 | return 0x0E; 361 | case KeyEvent.VK_BACK_QUOTE: 362 | return 0x0E; 363 | case KeyEvent.VK_1: 364 | return 0x16; 365 | case KeyEvent.VK_2: 366 | return 0x1E; 367 | case KeyEvent.VK_3: 368 | return 0x26; 369 | case KeyEvent.VK_4: 370 | return 0x25; 371 | case KeyEvent.VK_5: 372 | return 0x2E; 373 | case KeyEvent.VK_6: 374 | return 0x36; 375 | case KeyEvent.VK_7: 376 | return 0x3D; 377 | case KeyEvent.VK_8: 378 | return 0x3E; 379 | case KeyEvent.VK_9: 380 | return 0x46; 381 | case KeyEvent.VK_0: 382 | return 0x45; 383 | case KeyEvent.VK_MINUS: 384 | if(keyEvent.getKeyLocation() == KeyEvent.KEY_LOCATION_NUMPAD) return 0x7B; 385 | else return 0x4E; 386 | case KeyEvent.VK_UNDERSCORE: 387 | return 0x4E; 388 | case KeyEvent.VK_EQUALS: 389 | return 0x55; 390 | case KeyEvent.VK_PLUS: 391 | if(keyEvent.getKeyLocation() == KeyEvent.KEY_LOCATION_NUMPAD) return 0x79; 392 | else return 0x55; 393 | case KeyEvent.VK_BACK_SPACE: 394 | return 0x66; 395 | case KeyEvent.VK_TAB: 396 | return 0x0D; 397 | case KeyEvent.VK_Q: 398 | return 0x15; 399 | case KeyEvent.VK_W: 400 | return 0x1D; 401 | case KeyEvent.VK_E: 402 | return 0x24; 403 | case KeyEvent.VK_R: 404 | return 0x2D; 405 | case KeyEvent.VK_T: 406 | return 0x2C; 407 | case KeyEvent.VK_Y: 408 | return 0x35; 409 | case KeyEvent.VK_U: 410 | return 0x3C; 411 | case KeyEvent.VK_I: 412 | return 0x43; 413 | case KeyEvent.VK_O: 414 | return 0x44; 415 | case KeyEvent.VK_P: 416 | return 0x4D; 417 | case KeyEvent.VK_OPEN_BRACKET: 418 | return 0x54; 419 | case KeyEvent.VK_BRACELEFT: 420 | return 0x54; 421 | case KeyEvent.VK_CLOSE_BRACKET: 422 | return 0x5B; 423 | case KeyEvent.VK_BRACERIGHT: 424 | return 0x5B; 425 | case KeyEvent.VK_BACK_SLASH: 426 | return 0x5D; 427 | case KeyEvent.VK_CAPS_LOCK: 428 | return 0x58; 429 | case KeyEvent.VK_A: 430 | return 0x1C; 431 | case KeyEvent.VK_S: 432 | return 0x1B; 433 | case KeyEvent.VK_D: 434 | return 0x23; 435 | case KeyEvent.VK_F: 436 | return 0x2B; 437 | case KeyEvent.VK_G: 438 | return 0x34; 439 | case KeyEvent.VK_H: 440 | return 0x33; 441 | case KeyEvent.VK_J: 442 | return 0x3B; 443 | case KeyEvent.VK_K: 444 | return 0x32; 445 | case KeyEvent.VK_L: 446 | return 0x4B; 447 | case KeyEvent.VK_SEMICOLON: 448 | return 0x4C; 449 | case KeyEvent.VK_COLON: 450 | return 0x4C; 451 | case KeyEvent.VK_QUOTE: 452 | return 0x52; 453 | case KeyEvent.VK_QUOTEDBL: 454 | return 0x52; 455 | case KeyEvent.VK_ENTER: 456 | return 0x5A; // both regular and numpad enter map to 0x5A 457 | case KeyEvent.VK_SHIFT: 458 | if(keyEvent.getKeyLocation() == KeyEvent.KEY_LOCATION_RIGHT) return 0x59; 459 | else return 0x12; 460 | case KeyEvent.VK_Z: 461 | return 0x1A; 462 | case KeyEvent.VK_X: 463 | return 0x22; 464 | case KeyEvent.VK_C: 465 | return 0x21; 466 | case KeyEvent.VK_V: 467 | return 0x2A; 468 | case KeyEvent.VK_B: 469 | return 0x32; 470 | case KeyEvent.VK_N: 471 | return 0x31; 472 | case KeyEvent.VK_M: 473 | return 0x3A; 474 | case KeyEvent.VK_COMMA: 475 | return 0x41; 476 | case KeyEvent.VK_LESS: 477 | return 0x41; 478 | case KeyEvent.VK_PERIOD: 479 | if(keyEvent.getKeyLocation() == KeyEvent.KEY_LOCATION_NUMPAD) return 0x71; 480 | else return 0x49; 481 | case KeyEvent.VK_GREATER: 482 | return 0x49; 483 | case KeyEvent.VK_SLASH: 484 | return 0x4A; 485 | case KeyEvent.VK_CONTROL: 486 | if(keyEvent.getKeyLocation() == KeyEvent.KEY_LOCATION_RIGHT) return 0x14; 487 | else return 0x14; 488 | case KeyEvent.VK_WINDOWS: 489 | if(keyEvent.getKeyLocation() == KeyEvent.KEY_LOCATION_RIGHT) return 0x27; 490 | else return 0x1F; 491 | case KeyEvent.VK_ALT: 492 | if(keyEvent.getKeyLocation() == KeyEvent.KEY_LOCATION_RIGHT) return 0x11; 493 | else return 0x11; 494 | case KeyEvent.VK_SPACE: 495 | return 0x29; 496 | case KeyEvent.VK_CONTEXT_MENU: 497 | return 0x2F; 498 | case KeyEvent.VK_INSERT: 499 | return 0x70; 500 | case KeyEvent.VK_HOME: 501 | return 0x6C; 502 | case KeyEvent.VK_PAGE_UP: 503 | return 0x7D; 504 | case KeyEvent.VK_DELETE: 505 | return 0x71; 506 | case KeyEvent.VK_END: 507 | return 0x69; 508 | case KeyEvent.VK_PAGE_DOWN: 509 | return 0x7A; 510 | case KeyEvent.VK_UP: 511 | return 0x75; 512 | case KeyEvent.VK_KP_UP: 513 | return 0x75; 514 | case KeyEvent.VK_LEFT: 515 | return 0x6B; 516 | case KeyEvent.VK_KP_LEFT: 517 | return 0x6B; 518 | case KeyEvent.VK_DOWN: 519 | return 0x72; 520 | case KeyEvent.VK_KP_DOWN: 521 | return 0x72; 522 | case KeyEvent.VK_RIGHT: 523 | return 0x74; 524 | case KeyEvent.VK_KP_RIGHT: 525 | return 0x74; 526 | case KeyEvent.VK_NUM_LOCK: 527 | return 0x77; 528 | case KeyEvent.VK_ASTERISK: 529 | if(keyEvent.getKeyLocation() == KeyEvent.KEY_LOCATION_NUMPAD) return 0x7C; 530 | else return 0x3E; 531 | case KeyEvent.VK_NUMPAD1: 532 | return 0x69; 533 | case KeyEvent.VK_NUMPAD2: 534 | return 0x72; 535 | case KeyEvent.VK_NUMPAD3: 536 | return 0x7A; 537 | case KeyEvent.VK_NUMPAD4: 538 | return 0x6B; 539 | case KeyEvent.VK_NUMPAD5: 540 | return 0x73; 541 | case KeyEvent.VK_NUMPAD6: 542 | return 0x74; 543 | case KeyEvent.VK_NUMPAD7: 544 | return 0x6C; 545 | case KeyEvent.VK_NUMPAD8: 546 | return 0x75; 547 | case KeyEvent.VK_NUMPAD9: 548 | return 0x7D; 549 | case KeyEvent.VK_NUMPAD0: 550 | return 0x70; 551 | } 552 | return 0; 553 | } 554 | } 555 | -------------------------------------------------------------------------------- /src/EaterEmulator.java: -------------------------------------------------------------------------------- 1 | //Original Code by Dylan Speiser 2 | //https://github.com/DylanSpeiser 3 | 4 | import java.awt.*; 5 | import java.awt.event.ActionEvent; 6 | import java.awt.event.ActionListener; 7 | import java.io.File; 8 | import javax.swing.*; 9 | 10 | public class EaterEmulator extends JFrame implements ActionListener { 11 | public static String versionString = "2.11"; 12 | public static boolean debug = false; 13 | 14 | public static boolean verbose = false; 15 | public static boolean realisticKeyboard = false; 16 | public static int windowWidth = 1920, windowHeight = 1080; 17 | 18 | //Swing Things 19 | JPanel p = new JPanel(); 20 | JPanel header = new JPanel(); 21 | public static FileDialog fc = new java.awt.FileDialog((java.awt.Frame) null); 22 | public static JButton ROMopenButton = new JButton("Open ROM File"); 23 | public static JButton RAMopenButton = new JButton("Open RAM File"); 24 | 25 | public static JButton ShowLCDButton = new JButton("Show LCD"); 26 | public static JButton ShowGPUButton = new JButton("Show GPU"); 27 | public static JButton ShowSerialButton = new JButton("Show Serial"); 28 | public static JButton ResetButton = new JButton("Reset"); 29 | 30 | public static JButton optionsButton = new JButton("Options"); 31 | public static JButton keyboardButton= new JButton("Keyboard Mode"); 32 | 33 | public static JButton disassemblyButton = new JButton("Disassemble"); 34 | 35 | //Clock Stuff 36 | public static Thread clockThread; 37 | public static boolean clockState = false; 38 | public static int clocks = 0; 39 | public static boolean haltFlag = true; 40 | public static boolean slowerClock = false; 41 | public static boolean running = false; 42 | public static boolean keyboardMode = false; // False = controls (default), True = keyboard 43 | public static boolean carriageReturn = false; // False = \r or 0x0D, True = \n or 0x0A 44 | 45 | //Emulator Things 46 | public static EaterEmulator emu; 47 | public static RAM ram = new RAM(); 48 | public static ROM rom = new ROM(); 49 | public static LCD lcd = new LCD(); 50 | public static VIA via = new VIA(); 51 | public static ACIA acia = new ACIA(); 52 | public static CPU cpu = new CPU(); 53 | public static GPU gpu = new GPU(ram,false); 54 | public static SerialInterface serial = new SerialInterface(false); 55 | public static DisplayPanel GraphicsPanel = new DisplayPanel(); 56 | public static OptionsPane options = new OptionsPane(); 57 | public static DisassemblyOutput disOutput = new DisassemblyOutput(); 58 | 59 | public EaterEmulator() { 60 | //Swing Stuff: 61 | System.setProperty("sun.java2d.opengl", "true"); 62 | this.setSize(windowWidth, windowHeight); 63 | try { 64 | UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); 65 | }catch(Exception ex) { 66 | ex.printStackTrace(); 67 | } 68 | 69 | //Open .bin file button 70 | ROMopenButton.setVisible(true); 71 | ROMopenButton.addActionListener(this); 72 | ROMopenButton.setBounds(getWidth()-150, 15, 125, 25); 73 | ROMopenButton.setBackground(Color.white); 74 | GraphicsPanel.add(ROMopenButton); 75 | 76 | RAMopenButton.setVisible(true); 77 | RAMopenButton.addActionListener(this); 78 | RAMopenButton.setBounds(getWidth()-150, 45, 125, 25); 79 | RAMopenButton.setBackground(Color.white); 80 | GraphicsPanel.add(RAMopenButton); 81 | 82 | //Show Extra Windows buttons 83 | ShowLCDButton.setVisible(true); 84 | ShowLCDButton.addActionListener(this); 85 | ShowLCDButton.setBounds(getWidth()-300, 15, 125, 25); 86 | ShowLCDButton.setBackground(Color.white); 87 | GraphicsPanel.add(ShowLCDButton); 88 | 89 | ShowGPUButton.setVisible(true); 90 | ShowGPUButton.addActionListener(this); 91 | ShowGPUButton.setBounds(getWidth()-300, 45, 125, 25); 92 | ShowGPUButton.setBackground(Color.white); 93 | GraphicsPanel.add(ShowGPUButton); 94 | 95 | ShowSerialButton.setVisible(true); 96 | ShowSerialButton.addActionListener(this); 97 | ShowSerialButton.setBounds(getWidth()-450, 15, 125, 25); 98 | ShowSerialButton.setBackground(Color.white); 99 | GraphicsPanel.add(ShowSerialButton); 100 | 101 | ResetButton.setVisible(true); 102 | ResetButton.addActionListener(this); 103 | ResetButton.setBounds(getWidth()-450, 45, 125, 25); 104 | ResetButton.setBackground(Color.white); 105 | GraphicsPanel.add(ResetButton); 106 | 107 | //Options Button 108 | optionsButton.setVisible(true); 109 | optionsButton.addActionListener(this); 110 | optionsButton.setBounds(getWidth()-600, 15, 125, 25); 111 | optionsButton.setBackground(Color.white); 112 | GraphicsPanel.add(optionsButton); 113 | 114 | //Keyboard Mode Button 115 | keyboardButton.setVisible(true); 116 | keyboardButton.addActionListener(this); 117 | keyboardButton.setBounds(getWidth()-600, 45, 125, 25); 118 | keyboardButton.setBackground(Color.white); 119 | GraphicsPanel.add(keyboardButton); 120 | 121 | //Disassembly Button 122 | disassemblyButton.setVisible(true); 123 | disassemblyButton.addActionListener(this); 124 | disassemblyButton.setBounds(getWidth()-750, 15, 125, 25); 125 | disassemblyButton.setBackground(Color.white); 126 | GraphicsPanel.add(disassemblyButton); 127 | 128 | //file chooser 129 | fc.setDirectory(options.data.defaultFileChooserDirectory); 130 | fc.setVisible(false); 131 | 132 | //Clock thread setup 133 | clockThread = new Thread(() -> { 134 | while (true) { 135 | if (EaterEmulator.clockState) 136 | cpu.clock(); 137 | System.out.print(""); 138 | if (slowerClock) { 139 | try { 140 | Thread.sleep(1); 141 | } catch (InterruptedException e) {e.printStackTrace();} 142 | } 143 | } 144 | }); 145 | clockThread.start(); 146 | 147 | //Final Setup 148 | GraphicsPanel.setVisible(true); 149 | GraphicsPanel.frameTimer.addActionListener(this); 150 | options.setVisible(false); 151 | 152 | this.setTitle("65c02 Emulator"); 153 | this.setContentPane(GraphicsPanel); 154 | this.setVisible(true); 155 | this.setDefaultCloseOperation(EXIT_ON_CLOSE); 156 | 157 | options.updateSwingComponents(); 158 | options.applySwingValues(); 159 | } 160 | 161 | @Override 162 | public void actionPerformed(ActionEvent e) { 163 | if (e.getSource().equals(ROMopenButton)) { 164 | fc.setFile(""); 165 | fc.setMode(FileDialog.LOAD); 166 | fc.setVisible(true); 167 | 168 | if (fc.getFile() != null) 169 | rom.setROMArray(ROMLoader.readROM(new File(fc.getDirectory()+fc.getFile()))); 170 | 171 | GraphicsPanel.requestFocus(); 172 | GraphicsPanel.romPageString = EaterEmulator.rom.ROMString.substring(GraphicsPanel.romPage*960,(GraphicsPanel.romPage+1)*960); 173 | cpu.reset(); 174 | } else if (e.getSource().equals(RAMopenButton)) { 175 | fc.setFile(""); 176 | fc.setMode(FileDialog.LOAD); 177 | fc.setVisible(true); 178 | 179 | if (fc.getFile() != null) 180 | ram.setRAMArray(ROMLoader.readROM(new File(fc.getDirectory()+fc.getFile()))); 181 | 182 | GraphicsPanel.requestFocus(); 183 | GraphicsPanel.ramPageString = EaterEmulator.ram.RAMString.substring(GraphicsPanel.ramPage*960,(GraphicsPanel.ramPage+1)*960); 184 | cpu.reset(); 185 | } else if (e.getSource().equals(ShowLCDButton)) { 186 | lcd.setVisible(!lcd.isVisible()); 187 | } else if (e.getSource().equals(ShowGPUButton)) { 188 | gpu.setVisible(!gpu.isVisible()); 189 | } else if (e.getSource().equals(ShowSerialButton)) { 190 | serial.setVisible(!serial.isVisible()); 191 | } else if (e.getSource().equals(ResetButton)) { 192 | reset(); 193 | } else if (e.getSource().equals(optionsButton)) { 194 | options.setVisible(!options.isVisible()); 195 | } else if (e.getSource().equals(disassemblyButton)) { 196 | int startAddress = -1, endAddress = -1; 197 | while (true) { 198 | try { 199 | String startHex = JOptionPane.showInputDialog(null, "Enter starting hex address (max 2 bytes):", "Hex Input", JOptionPane.QUESTION_MESSAGE); 200 | if (startHex == null) break; // canceled 201 | 202 | startAddress = Integer.parseInt(startHex.trim(), 16); 203 | if (startAddress > 0xffff) throw new NumberFormatException("Out of range"); 204 | 205 | String endHex = JOptionPane.showInputDialog(null, "Enter final hex address (optional, defaults to FFFF):", "Hex Input", JOptionPane.QUESTION_MESSAGE); 206 | 207 | endAddress = (endHex == null || endHex.isEmpty()) ? 0xffff : Integer.parseInt(endHex.trim(), 16); 208 | if (endAddress > 0xffff) throw new NumberFormatException("Out of range"); 209 | 210 | 211 | break; 212 | } catch (NumberFormatException e_) { 213 | JOptionPane.showMessageDialog(null, "Invalid input! Please enter a valid hex address within two bytes.", "Error", JOptionPane.ERROR_MESSAGE); 214 | } 215 | } 216 | if (startAddress != -1 && endAddress != -1) { 217 | DisassemblyOutput.StartAddress = startAddress; 218 | DisassemblyOutput.EndAddress = endAddress; 219 | disOutput.setVisible(true); 220 | } 221 | } else if (e.getSource().equals(GraphicsPanel.frameTimer)) { 222 | if (!gpu.isVisible()) { 223 | ShowGPUButton.setText("Show GPU"); 224 | } else { 225 | ShowGPUButton.setText("Hide GPU"); 226 | } 227 | 228 | if (!lcd.isVisible()) { 229 | ShowLCDButton.setText("Show LCD"); 230 | } else { 231 | ShowLCDButton.setText("Hide LCD"); 232 | } 233 | 234 | if (!serial.isVisible()) { 235 | ShowSerialButton.setText("Show Serial"); 236 | } else { 237 | ShowSerialButton.setText("Hide Serial"); 238 | } 239 | } else if (e.getSource().equals(keyboardButton)) { 240 | keyboardMode = !keyboardMode; 241 | } 242 | 243 | 244 | } 245 | 246 | public static void reset() { 247 | cpu.reset(); 248 | lcd.reset(); 249 | via = new VIA(); 250 | acia = new ACIA(); 251 | ram = new RAM(); 252 | gpu.setRAM(ram); 253 | GraphicsPanel.ramPageString = ram.RAMString.substring(GraphicsPanel.ramPage*960,(GraphicsPanel.ramPage+1)*960); 254 | 255 | if (debug) 256 | if (verbose) System.out.println("Size: "+GraphicsPanel.getWidth()+" x "+GraphicsPanel.getHeight()); 257 | } 258 | 259 | public static void main(String[] args) { 260 | javax.swing.SwingUtilities.invokeLater(new Runnable() { 261 | public void run() { 262 | for (int i = 0; i < args.length; i++) { 263 | String s = args[i]; 264 | if (s.equals("-verbose")) verbose = true; 265 | else if (s.equals("-windowWidth")) { 266 | try { 267 | windowWidth = Integer.parseInt(args[++i]); 268 | } catch (Exception e) { 269 | System.out.println("Window width not understood, defaulting to " + windowWidth); 270 | } 271 | } 272 | else if (s.equals("-windowHeight")) { 273 | try { 274 | windowHeight = Integer.parseInt(args[++i]); 275 | } catch (Exception e) { 276 | System.out.println("Window height not understood, defaulting to " + windowHeight); 277 | } 278 | } 279 | else if (s.equals("-f")) { 280 | try { 281 | rom.setROMArray(ROMLoader.readROM(new File(args[++i]))); 282 | 283 | GraphicsPanel.requestFocus(); 284 | GraphicsPanel.romPageString = EaterEmulator.rom.ROMString.substring(GraphicsPanel.romPage*960,(GraphicsPanel.romPage+1)*960); 285 | cpu.reset(); 286 | } catch (Exception e) { 287 | System.out.println("There was an error loading the ROM file specified with -f"); 288 | } 289 | } 290 | } 291 | 292 | //printing done after all arguments incase verbose argument comes after the other arguments 293 | if (verbose) System.out.println("Running in verbose mode!"); 294 | if (verbose) System.out.println("Resolution of " + windowWidth + "x" + windowHeight + "."); 295 | 296 | emu = new EaterEmulator(); 297 | } 298 | }); 299 | } 300 | } 301 | -------------------------------------------------------------------------------- /src/GPU.java: -------------------------------------------------------------------------------- 1 | import java.awt.Color; 2 | import java.awt.Graphics; 3 | import java.awt.image.*; 4 | import java.awt.event.ActionEvent; 5 | import java.awt.event.ActionListener; 6 | import java.io.File; 7 | import java.io.InputStream; 8 | import java.nio.file.*; 9 | import java.util.Scanner; 10 | 11 | import javax.swing.*; 12 | 13 | public class GPU extends JFrame implements ActionListener { 14 | GPUPanel p = new GPUPanel(); 15 | 16 | Timer t; 17 | Scanner s; 18 | 19 | boolean scanned = false; 20 | 21 | public static int VRAM_START_ADDRESS = 0x6000; 22 | 23 | public static int n_cols = 80; 24 | public static int n_rows = 60; 25 | 26 | public static int width = 100; 27 | public static int height = 75; 28 | 29 | public static int gpuMode = 1; 30 | public static int GPUPixelScale = 8; 31 | 32 | int charWidth; 33 | int charHeight; 34 | 35 | int effectiveCharWidth; 36 | int effectiveCharHeight; 37 | 38 | boolean debug = false; 39 | 40 | public static InputStream charsetStream; 41 | BufferedImage[] charImages = new BufferedImage[256]; 42 | 43 | RAM vram; 44 | 45 | public GPU(RAM vram,boolean isVisible) { 46 | this.setSize(GPUPixelScale*width,(GPUPixelScale*height)+30); //+30 for title bar 47 | 48 | t = new Timer(16,this); 49 | t.start(); 50 | 51 | this.vram = vram; 52 | 53 | charWidth = 8; 54 | charHeight = 8; 55 | 56 | effectiveCharWidth = width/n_cols; 57 | effectiveCharHeight = height/n_rows; 58 | 59 | if (debug) 60 | if (EaterEmulator.verbose) System.out.println("charWidth, charHeight = "+charWidth+", "+charHeight); 61 | 62 | charsetStream = this.getClass().getClassLoader().getResourceAsStream("DylSCII.bin"); 63 | byte[] charsetBytes = {}; 64 | 65 | try { 66 | charsetBytes = charsetStream.readAllBytes(); 67 | } catch (Exception e) { 68 | e.printStackTrace(); 69 | System.err.println("Failed to read charset."); 70 | System.exit(ABORT); 71 | } 72 | 73 | for (int i = 0; i < charImages.length; i++) { 74 | charImages[i] = new BufferedImage(8,8,BufferedImage.TYPE_BYTE_BINARY); 75 | if (debug) 76 | System.out.print("Byte index: "+(i*charHeight)+" "); 77 | 78 | for (int y = 0; y < charHeight; y++) { 79 | byte lineData = charsetBytes[i*charHeight + y]; 80 | 81 | for (int x = 0; x < charWidth; x++) { 82 | Graphics gr = charImages[i].getGraphics(); 83 | gr.setColor(getBit(lineData,7-x)==1 ? Color.WHITE : Color.black); 84 | gr.drawRect(x, y, 1, 1); 85 | 86 | //charImages[i].setRGB(x, y, getBit(lineData,x)==1 ? 255 : 0); 87 | } 88 | 89 | if (debug) 90 | System.out.print(ROMLoader.byteToHexString(lineData)+" "); 91 | } 92 | 93 | // if (debug) { 94 | // try { 95 | // ImageIO.write(charImages[i], "png", new File("charImg/"+i+".png")); 96 | // } catch (Exception e) { 97 | // e.printStackTrace(); 98 | // if (EaterEmulator.verbose) System.out.println("ERROR WRITING CHAR IMAGE"); 99 | // } 100 | // } 101 | 102 | if (debug) 103 | if (EaterEmulator.verbose) System.out.println(); 104 | } 105 | 106 | if (debug) { 107 | for (int i = 0; i < 255; i++) 108 | vram.write((short)i, (byte)i); 109 | } 110 | 111 | this.setTitle("GPU"); 112 | this.setContentPane(p); 113 | this.setAlwaysOnTop(true); 114 | this.setVisible(isVisible); 115 | this.setDefaultCloseOperation(HIDE_ON_CLOSE); 116 | this.setResizable(false); 117 | } 118 | 119 | public GPU(boolean isVisible) { 120 | this(new RAM(),isVisible); 121 | } 122 | 123 | public static void main(String[] args) { 124 | GPU gpu = new GPU(true); 125 | gpu.setVisible(true); 126 | 127 | @SuppressWarnings("resource") 128 | Scanner scan = new Scanner(System.in); 129 | if (EaterEmulator.verbose) System.out.println("Enter the path to a .bin file with character data:"); 130 | 131 | while (true) { 132 | String input = scan.nextLine(); 133 | 134 | File f = new File(input); 135 | byte[] newData = new byte[0]; 136 | byte[] newRAMArray = new byte[0x8000]; 137 | 138 | if (EaterEmulator.verbose) System.out.println("Created new RAM Array with "+newRAMArray.length+" bytes."); 139 | 140 | try { 141 | newData = Files.readAllBytes(f.toPath()); 142 | } catch (Exception e) { 143 | e.printStackTrace(); 144 | System.err.println("Failed to read character data."); 145 | System.exit(ABORT); 146 | } 147 | 148 | if (EaterEmulator.verbose) System.out.println("Read "+newData.length+" bytes."); 149 | 150 | System.arraycopy(newData, 0, newRAMArray, (gpuMode == 1 | gpuMode == 2) ? 0 : (GPU.VRAM_START_ADDRESS), Math.min(newData.length,newRAMArray.length)); 151 | 152 | gpu.vram.setRAMArray(newRAMArray); 153 | 154 | gpu.scanned = true; 155 | // if (EaterEmulator.verbose) System.out.println(ROMLoader.ROMString(gpu.vram.getRAMArray(),40,false)); 156 | // System.exit(0); 157 | } 158 | } 159 | 160 | public void reset() { 161 | p.repaint(); 162 | } 163 | 164 | public class GPUPanel extends JPanel { 165 | public GPUPanel() { 166 | 167 | } 168 | 169 | public void paintComponent(Graphics g) { 170 | if (this.isVisible()) { 171 | g.setColor(Color.BLACK); 172 | g.fillRect(0, 0, p.getWidth(), p.getHeight()); 173 | 174 | g.setColor(Color.white); 175 | 176 | if (gpuMode == 0) { 177 | //Speiser Character Mode 178 | for (int i = 0; i> 3))+ 200 | " j (shifted): "+Integer.toBinaryString((j >> 1))+ 201 | " index: "+Integer.toBinaryString(index)); 202 | */ 203 | 204 | byte pixelData = vram.read( (short) Math.min((((gpuMode == 1 || gpuMode == 2) && debug == true) ? 0 : VRAM_START_ADDRESS)+index,vram.getRAMArray().length-1) ); 205 | //pixelData = 0b001010; 206 | 207 | byte red = (byte)((pixelData & 0b00110000) >> 4); 208 | byte green = (byte)((pixelData & 0b00001100) >> 2); 209 | byte blue = (byte)((pixelData & 0b00000011) >> 0); 210 | 211 | Color c = Color.decode("#"+ 212 | Integer.toHexString(red*5)+Integer.toHexString(red*5)+ 213 | Integer.toHexString(green*5)+Integer.toHexString(green*5)+ 214 | Integer.toHexString(blue*5)+Integer.toHexString(blue*5) 215 | ); 216 | 217 | if (scanned && debug) { 218 | if (EaterEmulator.verbose) System.out.println("PixelData: "+ROMLoader.byteToHexString(pixelData)+" Color "+c.toString()+" @ ("+i+","+j+"), Index "+Integer.toHexString(index)); 219 | if (EaterEmulator.verbose) System.out.println("PixelData "+Integer.toBinaryString(pixelData)+" R:"+Integer.toBinaryString(red)+" G:"+Integer.toBinaryString(green)+" B:"+Integer.toBinaryString(blue)); 220 | if (EaterEmulator.verbose) System.out.println("NextPowerOf2: "+nextPowerOf2); 221 | } 222 | 223 | g.setColor(c); 224 | 225 | g.fillRect(GPUPixelScale*j, GPUPixelScale*i, GPUPixelScale, GPUPixelScale); 226 | } 227 | } 228 | } 229 | } 230 | } 231 | } 232 | 233 | public void setRAM(RAM r) { 234 | this.vram = r; 235 | } 236 | 237 | @Override 238 | public void actionPerformed(ActionEvent arg0) { 239 | if (arg0.getSource().equals(t)) { 240 | p.repaint(); 241 | } 242 | } 243 | 244 | private static byte getBit(byte b, int position) { 245 | return (byte)((b >> position) & 1); 246 | } 247 | } 248 | -------------------------------------------------------------------------------- /src/Instruction.java: -------------------------------------------------------------------------------- 1 | public class Instruction { 2 | public OpCode opcode; 3 | public AddressMode addressMode; 4 | public int cycles; 5 | public boolean wdc; // WDC extensions (65c02) 6 | 7 | public Instruction(OpCode opcode, AddressMode addressMode, int cycles, boolean wdc) { 8 | this.opcode = opcode; 9 | this.addressMode = addressMode; 10 | this.cycles = cycles; 11 | this.wdc = wdc; 12 | } 13 | 14 | @Override 15 | public String toString() { 16 | return opcode+","+addressMode; 17 | } 18 | } 19 | 20 | -------------------------------------------------------------------------------- /src/LCD.java: -------------------------------------------------------------------------------- 1 | import java.awt.Color; 2 | import java.awt.Font; 3 | import java.awt.FontFormatException; 4 | import java.awt.Graphics; 5 | import java.awt.GraphicsEnvironment; 6 | import java.awt.event.ActionEvent; 7 | import java.awt.event.ActionListener; 8 | import java.io.IOException; 9 | import java.util.Arrays; 10 | import java.util.Scanner; 11 | 12 | import javax.swing.*; 13 | 14 | public class LCD extends JFrame implements ActionListener { 15 | LCDPanel p = new LCDPanel(); 16 | Timer t; 17 | Timer cursorTimer; 18 | Font lcdFont; 19 | Scanner s; 20 | 21 | boolean graphicalCursorBlinkFlag = false; 22 | boolean bigMode = false; 23 | 24 | boolean debug = false; 25 | 26 | int cols = 16; 27 | int rows = 2; 28 | 29 | //Internal flags 30 | int cursorPos = 0; 31 | boolean increment = true; 32 | boolean displayPower = false; 33 | boolean cursor = false; 34 | boolean cursorBlink = false; 35 | boolean fourBitMode = false; 36 | 37 | char[] text = new char[80]; 38 | 39 | public LCD() { 40 | this.setSize(565,185); 41 | t = new Timer(100,this); 42 | t.start(); 43 | cursorTimer = new Timer(500,this); 44 | cursorTimer.start(); 45 | 46 | String s = ""; 47 | 48 | for (int i = 0; i < 80; i++) { 49 | if (i= 0 && newPos < text.length) { 128 | cursorPos = (data & 0b01111111); 129 | } else { 130 | cursorPos = 0; 131 | } 132 | 133 | } else if ((data & 0b01000000) == 0b01000000) { 134 | //Set CGRAM Address 135 | if (EaterEmulator.verbose) System.out.println("LCD: Tried to set CGRAM Address, that is unimplemented!"); 136 | 137 | } else if ((data & 0b00100000) == 0b00100000) { 138 | //FUNCTION SET 139 | if ((data & 0b00010000) == 0b00010000) { 140 | fourBitMode = false; 141 | } else { 142 | fourBitMode = true; 143 | } 144 | if (debug) 145 | if (EaterEmulator.verbose) System.out.println("Function: Four Bit Mode: "+fourBitMode); 146 | } else if ((data & 0b00010000) == 0b00010000) { 147 | //SHIFT 148 | boolean rightleft = false; 149 | if ((data & 0b00000100) == 0b00000100) { 150 | //RIGHT 151 | rightleft = true; 152 | } else { 153 | //LEFT 154 | rightleft = false; 155 | } 156 | if ((data & 0b00001000) == 0b00001000) { 157 | //SCREEN 158 | char[] newCharArray = new char[40]; 159 | Arrays.fill(newCharArray, ' '); 160 | if (debug) 161 | if (EaterEmulator.verbose) System.out.println("Shifted screen to the "+(rightleft ? "right." : "left.")); 162 | } else { 163 | //CURSOR 164 | cursorPos += (rightleft ? 1 : -1); 165 | if (cursorPos < 0) 166 | cursorPos = 0; 167 | if (debug) { 168 | if (EaterEmulator.verbose) System.out.println("Shifted cursor to the "+(rightleft ? "right." : "left.")); 169 | if (EaterEmulator.verbose) System.out.println("CursorPos: "+cursorPos); 170 | } 171 | } 172 | } else if ((data & 0b00001000) == 0b00001000) { 173 | //DISPLAY CONTROL 174 | if ((data & 0b00000100) == 0b00000100) { 175 | displayPower = true; 176 | } else { 177 | displayPower = false; 178 | if (debug) 179 | if (EaterEmulator.verbose) System.out.println("Turned the Display off! | "+Integer.toBinaryString(Byte.toUnsignedInt(data))); 180 | } 181 | if ((data & 0b00000010) == 0b00000010) { 182 | cursor = true; 183 | } else { 184 | cursor = false; 185 | } 186 | if ((data & 0b00000001) == 0b00000001) { 187 | cursorBlink = true; 188 | } else { 189 | cursorBlink = false; 190 | } 191 | if (debug) 192 | if (EaterEmulator.verbose) System.out.println("Display Control: Power: "+displayPower+" Cursor: "+cursor+" Blink: "+cursorBlink); 193 | } else if ((data & 0b00000100) == 0b00000100) { 194 | //ENTRY MODE SET 195 | if ((data & 0b00000010) == 0b00000010) { 196 | increment = true; 197 | } else { 198 | increment = false; 199 | } 200 | if (debug) 201 | if (EaterEmulator.verbose) System.out.println("Set Entry Mode: Increment: "+increment); 202 | } else if ((data & 0b00000010) == 0b00000010) { 203 | //RETURN HOME 204 | cursorPos = 0; 205 | if (debug) 206 | if (EaterEmulator.verbose) System.out.println("Return Home"); 207 | } else if (data == 0b00000001) { 208 | //CLEAR 209 | cursorPos = 0; 210 | text = new char[80]; 211 | for (int i = 0; i < 80; i++) { 212 | text[i] = ' '; 213 | } 214 | if (debug) 215 | if (EaterEmulator.verbose) System.out.println("Cleared!"); 216 | } else { 217 | if (EaterEmulator.verbose) System.out.println("Tried to do invalid instruction "+ROMLoader.byteToHexString(data)); 218 | } 219 | } else { 220 | //DATA 221 | text[cursorPos] = (char)data; 222 | int prevCursorPos = cursorPos; 223 | cursorPos += increment ? 1 : -1; 224 | if (cursorPos >= text.length) { 225 | cursorPos = 0; 226 | } else if (cursorPos < 0) { 227 | cursorPos = 0; 228 | } 229 | if (debug) 230 | if (EaterEmulator.verbose) System.out.println("Data: Wrote "+(char)data+" at "+prevCursorPos); 231 | } 232 | } 233 | 234 | //Read from LCD 235 | public byte read(boolean regSel) { 236 | if (debug) 237 | if (EaterEmulator.verbose) System.out.println("Reading from LCD with regSel"+(regSel ? '1':'0')); 238 | 239 | byte retVal = 0; 240 | 241 | if (regSel) { 242 | //Read from RAM 243 | retVal = (byte)(text[cursorPos]); 244 | } else { 245 | //Read busy flag and address 246 | retVal = (byte)(127 & cursorPos); 247 | } 248 | 249 | return retVal; 250 | } 251 | 252 | public class LCDPanel extends JPanel { 253 | public LCDPanel() { 254 | try { 255 | lcdFont = Font.createFont(Font.TRUETYPE_FONT,this.getClass().getClassLoader().getResourceAsStream("5x8_lcd_hd44780u_a02.ttf")).deriveFont(47f); 256 | GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); 257 | ge.registerFont(lcdFont); 258 | } catch (FontFormatException | IOException e) { 259 | e.printStackTrace(); 260 | if (EaterEmulator.verbose) System.out.println("Error loading LCD Font!"); 261 | } 262 | } 263 | 264 | public void paintComponent(Graphics g) { 265 | g.setColor(Color.getHSBColor(0.62f, 0.83f, 1f)); 266 | g.fillRect(0, 0, p.getWidth(), p.getHeight()); 267 | g.setColor(Color.getHSBColor(0.62f, 0.87f, 0.78f)); 268 | for (int i = 0; i SwingComponentsList = new ArrayList(); 28 | FileDialog fc = new java.awt.FileDialog((java.awt.Frame) null); 29 | JButton openOptionsFileButton = new JButton("Open File"); 30 | JTextField openOptionsFileText = new JTextField(data.defaultSaveDirectory+"options.pref"); 31 | JLabel openOptionsFileLabel = new JLabel("Load options from file"); 32 | 33 | JLabel DefaultFileChooserPathOptionLabel = new JLabel("Default File Chooser Path"); 34 | JTextField DefaultFileChooserPathOptionTextField = new JTextField(data.defaultFileChooserDirectory); 35 | 36 | JLabel VIAAddressOptionLabel = new JLabel("VIA Address"); 37 | JTextField VIAAddressOptionTextField = new JTextField(""+data.VIA_Address); 38 | JLabel VIAAddressOptionHexLabel = new JLabel(Integer.toHexString(data.VIA_Address)); 39 | 40 | JLabel ACIAAddressOptionLabel = new JLabel("ACIA Address"); 41 | JTextField ACIAAddressOptionTextField = new JTextField(""+data.ACIA_Address); 42 | JLabel ACIAAddressOptionHexLabel = new JLabel(Integer.toHexString(data.ACIA_Address)); 43 | 44 | JLabel GPUResolutionOptionLabel = new JLabel("GPU Resolution (WxH)"); 45 | JTextField GPUHeightTextField = new JTextField(""+data.GPUHeight); 46 | JTextField GPUWidthTextField = new JTextField(""+data.GPUWidth); 47 | 48 | JLabel GPUCharGridOptionLabel = new JLabel("GPU Character Grid (ColsxRows)"); 49 | JTextField GPUColsTextField = new JTextField(""+data.GPUCols); 50 | JTextField GPURowsTextField = new JTextField(""+data.GPURows); 51 | 52 | JLabel GPUBufferAddressOptionLabel = new JLabel("GPU Char/Pixel Buffer Start Address"); 53 | JTextField GPUBufferAddressOptionTextField = new JTextField(""+data.GPUBufferBegin); 54 | JLabel GPUBufferAddressOptionHexLabel = new JLabel(Integer.toHexString(data.GPUBufferBegin)); 55 | 56 | JLabel GPUModeOptionLabel = new JLabel("GPU Mode"); 57 | JTextField GPUModeOptionTextField = new JTextField(""+data.GPUMode); 58 | 59 | JLabel GPUModeExplanationLabel = new JLabel("GPU Mode 0 = Speiser Character Mode (customizable)
GPU Mode 1 = Eater Bitmap Mode (100x75, 64 colors, Buffer @ $2000)
GPU Mode 2 = Eater Bitmap Mode (customizable)"); 60 | 61 | JLabel GPUBitmapPixelScaleLabel = new JLabel("GPU Bitmap Pixel Scale: "); 62 | JTextField GPUBitmapPixelScaleTextField = new JTextField(""+data.GPUMode); 63 | 64 | JLabel LineEndingLabel = new JLabel("Serial Monitor Line Ending: "); 65 | ButtonGroup lineEndingButtonGroup = new ButtonGroup(); 66 | JRadioButton LineEndingRadioR = new JRadioButton("\\r, 0x0D"); 67 | JRadioButton LineEndingRadioN = new JRadioButton("\\n, 0x0A"); 68 | 69 | JLabel LCDModeLabel = new JLabel("LCD Mode: "); 70 | ButtonGroup lcdModeButtonGroup = new ButtonGroup(); 71 | JRadioButton LCDModeRadioSmall = new JRadioButton("16x2"); 72 | JRadioButton LCDModeRadioLarge = new JRadioButton("20x4"); 73 | 74 | JLabel KeyboardLocationLabel = new JLabel("Keyboard Memory Location (For Simplified Keyboard Mode): "); 75 | JTextField KeyboardLocationTextField = new JTextField(""+data.keyboardLocation); 76 | JLabel KeyboardLocationHexLabel = new JLabel(Integer.toHexString(data.GPUBufferBegin)); 77 | 78 | JLabel KeyboardModeOptionLabel = new JLabel("Keyboard Mode"); 79 | JTextField KeyboardModeOptionTextField = new JTextField(""+data.keyboardMode); 80 | JLabel KeyboardModeExplanationLabel = new JLabel("Keyboard Mode 0 = Simplified Keyboard Mode
Keyboard Mode 1 = Realistic Keyboard Mode"); 81 | 82 | JLabel ForegroundColorLabel = new JLabel("Foreground Color"); 83 | JTextField ForegroundColorChooser = new JTextField("#"+Integer.toHexString(data.fgColor.getRGB()).substring(2)); 84 | 85 | JLabel BackgroundColorLabel = new JLabel("Background Color"); 86 | JTextField BackgroundColorChooser = new JTextField("#"+Integer.toHexString(data.bgColor.getRGB()).substring(2)); 87 | 88 | int VRAMSize; 89 | JLabel VRAMSizeLabel = new JLabel("("+VRAMSize+"bytes)"); 90 | JLabel VRAMRangeLabel = new JLabel("VRAM Range: $"+Integer.toHexString(data.GPUBufferBegin)+"-$"+Integer.toHexString(data.GPUBufferBegin+VRAMSize-1)); 91 | 92 | JButton applyOptionsButton = new JButton("Apply Options"); 93 | JButton saveOptionsButton = new JButton("Save Options to File"); 94 | 95 | public OptionsPane() { 96 | this.setSize(700,780); 97 | t = new Timer(16,this); 98 | t.start(); 99 | 100 | //File chooser 101 | try { 102 | Files.createDirectories(Paths.get(data.defaultSaveDirectory)); 103 | } catch (IOException e) { 104 | e.printStackTrace(); 105 | } 106 | 107 | optionsFile = new File(data.defaultSaveDirectory+"options.pref"); 108 | 109 | writeDataToFile(new File(data.defaultSaveDirectory+"defaults.pref")); 110 | readDataFromFile(optionsFile); 111 | writeDataToFile(optionsFile); 112 | 113 | fc.setVisible(false); 114 | fc.setDirectory(data.defaultFileChooserDirectory); 115 | 116 | //Swing Components 117 | SwingComponentsList.add(openOptionsFileButton); 118 | SwingComponentsList.add(openOptionsFileText); 119 | SwingComponentsList.add(openOptionsFileLabel); 120 | SwingComponentsList.add(VIAAddressOptionLabel); 121 | SwingComponentsList.add(VIAAddressOptionTextField); 122 | SwingComponentsList.add(VIAAddressOptionHexLabel); 123 | SwingComponentsList.add(ACIAAddressOptionLabel); 124 | SwingComponentsList.add(ACIAAddressOptionTextField); 125 | SwingComponentsList.add(ACIAAddressOptionHexLabel); 126 | SwingComponentsList.add(GPUResolutionOptionLabel); 127 | SwingComponentsList.add(GPUHeightTextField); 128 | SwingComponentsList.add(GPUWidthTextField); 129 | SwingComponentsList.add(GPUBufferAddressOptionLabel); 130 | SwingComponentsList.add(GPUBufferAddressOptionTextField); 131 | SwingComponentsList.add(GPUModeOptionLabel); 132 | SwingComponentsList.add(GPUModeOptionTextField); 133 | SwingComponentsList.add(applyOptionsButton); 134 | SwingComponentsList.add(saveOptionsButton); 135 | SwingComponentsList.add(DefaultFileChooserPathOptionLabel); 136 | SwingComponentsList.add(DefaultFileChooserPathOptionTextField); 137 | SwingComponentsList.add(GPUBufferAddressOptionHexLabel); 138 | SwingComponentsList.add(GPUCharGridOptionLabel); 139 | SwingComponentsList.add(GPUColsTextField); 140 | SwingComponentsList.add(GPURowsTextField); 141 | SwingComponentsList.add(ForegroundColorLabel); 142 | SwingComponentsList.add(ForegroundColorChooser); 143 | SwingComponentsList.add(BackgroundColorLabel); 144 | SwingComponentsList.add(BackgroundColorChooser); 145 | SwingComponentsList.add(VRAMRangeLabel); 146 | SwingComponentsList.add(VRAMSizeLabel); 147 | SwingComponentsList.add(GPUModeExplanationLabel); 148 | SwingComponentsList.add(GPUBitmapPixelScaleLabel); 149 | SwingComponentsList.add(GPUBitmapPixelScaleTextField); 150 | SwingComponentsList.add(KeyboardLocationLabel); 151 | SwingComponentsList.add(KeyboardLocationTextField); 152 | SwingComponentsList.add(KeyboardLocationHexLabel); 153 | SwingComponentsList.add(KeyboardModeOptionLabel); 154 | SwingComponentsList.add(KeyboardModeOptionTextField); 155 | SwingComponentsList.add(KeyboardModeExplanationLabel); 156 | SwingComponentsList.add(LCDModeLabel); 157 | SwingComponentsList.add(LCDModeRadioLarge); 158 | SwingComponentsList.add(LCDModeRadioSmall); 159 | SwingComponentsList.add(LineEndingLabel); 160 | SwingComponentsList.add(LineEndingRadioR); 161 | SwingComponentsList.add(LineEndingRadioN); 162 | 163 | this.setTitle("Options"); 164 | this.setContentPane(p); 165 | this.setAlwaysOnTop(true); 166 | this.setVisible(true); 167 | this.setDefaultCloseOperation(HIDE_ON_CLOSE); 168 | this.setResizable(false); 169 | 170 | p.setBackground(new Color(200,200,200)); 171 | 172 | for (JComponent component : SwingComponentsList) { 173 | component.setVisible(true); 174 | component.setBackground(Color.white); 175 | if (component instanceof JButton) { 176 | ((JButton)(component)).addActionListener(this); 177 | } 178 | p.add(component); 179 | } 180 | 181 | lcdModeButtonGroup.add(LCDModeRadioSmall); 182 | lcdModeButtonGroup.add(LCDModeRadioLarge); 183 | 184 | lineEndingButtonGroup.add(LineEndingRadioR); 185 | lineEndingButtonGroup.add(LineEndingRadioN); 186 | 187 | //Swing Positioning 188 | resetSwingPositions(); 189 | } 190 | 191 | // @SuppressWarnings("unused") 192 | // public static void main(String[] args) { 193 | // OptionsPane options = new OptionsPane(); 194 | // } 195 | 196 | public void reset() { 197 | p.repaint(); 198 | resetSwingPositions(); 199 | } 200 | 201 | public class OptionsPanel extends JPanel { 202 | public OptionsPanel() { 203 | 204 | } 205 | 206 | public void paintComponent(Graphics g) { 207 | g.setColor(getBackground()); 208 | g.fillRect(0, 0, p.getWidth(), p.getHeight()); 209 | 210 | g.setColor(getForeground()); 211 | } 212 | } 213 | 214 | @Override 215 | public void actionPerformed(ActionEvent arg0) { 216 | if (arg0.getSource().equals(t)) { 217 | p.repaint(); 218 | 219 | resetSwingPositions(); 220 | 221 | //VIA Address Hex Decoding 222 | int VIAaddr = 0; 223 | 224 | try { 225 | VIAaddr = Integer.parseInt(VIAAddressOptionTextField.getText().equals("") ? "0" : VIAAddressOptionTextField.getText()); 226 | } catch (Exception e) { 227 | e.printStackTrace(); 228 | }; 229 | 230 | VIAAddressOptionHexLabel.setText("= $"+Integer.toHexString(VIAaddr)); 231 | 232 | //ACIA Address Hex Decoding 233 | int ACIAaddr = 0; 234 | 235 | try { 236 | ACIAaddr = Integer.parseInt(ACIAAddressOptionTextField.getText().equals("") ? "0" : ACIAAddressOptionTextField.getText()); 237 | } catch (Exception e) { 238 | e.printStackTrace(); 239 | }; 240 | 241 | ACIAAddressOptionHexLabel.setText("= $"+Integer.toHexString(ACIAaddr)); 242 | 243 | //Video Buffer Start Address Hex Decoding 244 | int VBStartAddr = 0; 245 | 246 | try { 247 | VBStartAddr = Integer.parseInt(GPUBufferAddressOptionTextField.getText().equals("") ? "0" : GPUBufferAddressOptionTextField.getText()); 248 | } catch (Exception e) { 249 | e.printStackTrace(); 250 | }; 251 | 252 | GPUBufferAddressOptionHexLabel.setText("= $"+Integer.toHexString(VBStartAddr)); 253 | 254 | //Keyboard Address Hex Decoding 255 | int KBAddr = 0; 256 | 257 | try { 258 | KBAddr = Integer.parseInt(KeyboardLocationTextField.getText().equals("") ? "0" : KeyboardLocationTextField.getText()); 259 | } catch (Exception e) { 260 | e.printStackTrace(); 261 | }; 262 | 263 | KeyboardLocationHexLabel.setText("= $"+Integer.toHexString(KBAddr)); 264 | 265 | //VRAM Size 266 | if (data.GPUMode == 0) { 267 | VRAMSize = data.GPUCols*data.GPURows; 268 | } else if (data.GPUMode == 1 || data.GPUMode == 2) { 269 | data.GPUWidth = 100; 270 | data.GPUHeight = 75; 271 | data.GPUBufferBegin = 8192; 272 | VRAMSize = data.GPUWidth*data.GPUHeight; 273 | } else { 274 | VRAMSize = 0; 275 | } 276 | 277 | VRAMSizeLabel.setText("("+VRAMSize+" bytes)"); 278 | VRAMRangeLabel.setText("VRAM Range: $"+Integer.toHexString(VBStartAddr)+" - $"+Integer.toHexString(VBStartAddr+VRAMSize-1)); 279 | } 280 | 281 | //Buttons 282 | if (arg0.getSource().equals(openOptionsFileButton)) { 283 | fc.setDirectory(openOptionsFileText.getText()); 284 | fc.setFile(""); 285 | 286 | fc.setMode(FileDialog.LOAD); 287 | fc.setVisible(true); 288 | 289 | if (fc.getFile() != null) { 290 | optionsFile = new File(fc.getDirectory()+fc.getFile()); 291 | openOptionsFileText.setText(optionsFile.getAbsolutePath()); 292 | } 293 | 294 | readDataFromFile(optionsFile); 295 | applySwingValues(); 296 | updateSwingComponents(); 297 | } else 298 | if (arg0.getSource().equals(applyOptionsButton)) { 299 | applySwingValues(); 300 | updateSwingComponents(); 301 | } else 302 | if (arg0.getSource().equals(saveOptionsButton)) { 303 | File saveFile = new File(openOptionsFileText.getText()); 304 | 305 | fc.setDirectory(saveFile.getParent()); 306 | fc.setFile(saveFile.getName()); 307 | 308 | fc.setMode(FileDialog.SAVE); 309 | 310 | fc.setVisible(true); 311 | File newOptionsFile; 312 | 313 | if (fc.getFile() != null) { 314 | newOptionsFile = saveFile; 315 | writeDataToFile(newOptionsFile); 316 | } 317 | } 318 | } 319 | 320 | private void readDataFromFile(File f) { 321 | try { 322 | OptionsData newData = null; 323 | 324 | FileInputStream fileIn = new FileInputStream(f); 325 | ObjectInputStream in = new ObjectInputStream(fileIn); 326 | newData = (OptionsData) in.readObject(); 327 | in.close(); 328 | fileIn.close(); 329 | 330 | data = newData; 331 | updateSwingComponents(); 332 | } catch (Exception e) { 333 | if (EaterEmulator.verbose) System.out.println("Reading options data failed!"); 334 | e.printStackTrace(); 335 | } 336 | } 337 | 338 | public void updateSwingComponents() { 339 | DefaultFileChooserPathOptionTextField.setText(data.defaultFileChooserDirectory); 340 | VIAAddressOptionTextField.setText(""+data.VIA_Address); 341 | ACIAAddressOptionTextField.setText(""+data.ACIA_Address); 342 | GPUWidthTextField.setEditable(!(data.GPUMode == 1)); 343 | GPUHeightTextField.setEditable(!(data.GPUMode == 1)); 344 | GPUBufferAddressOptionTextField.setEditable(!(data.GPUMode == 1)); 345 | 346 | if (data.GPUMode == 1) { 347 | GPUWidthTextField.setText(""+100); 348 | GPUHeightTextField.setText(""+75); 349 | GPUBufferAddressOptionTextField.setText(""+8192); 350 | } else { 351 | GPUWidthTextField.setText(""+data.GPUWidth); 352 | GPUHeightTextField.setText(""+data.GPUHeight); 353 | GPUBufferAddressOptionTextField.setText(""+data.GPUBufferBegin); 354 | } 355 | 356 | GPUColsTextField.setText(""+data.GPUCols); 357 | GPURowsTextField.setText(""+data.GPURows); 358 | 359 | KeyboardModeOptionTextField.setText(""+data.keyboardMode); 360 | 361 | GPUModeOptionTextField.setText(""+data.GPUMode); 362 | GPUBitmapPixelScaleTextField.setText(""+data.GPUBitmapPixelScale); 363 | ForegroundColorChooser.setText("#"+Integer.toHexString(data.fgColor.getRGB()).substring(2)); 364 | BackgroundColorChooser.setText("#"+Integer.toHexString(data.bgColor.getRGB()).substring(2)); 365 | 366 | LCDModeRadioSmall.setSelected(!data.lcdBigMode); 367 | LCDModeRadioLarge.setSelected(data.lcdBigMode); 368 | 369 | LineEndingRadioR.setSelected(data.carriageReturn); 370 | LineEndingRadioN.setSelected(!data.carriageReturn); 371 | } 372 | 373 | private void writeDataToFile(File f) { 374 | try { 375 | FileOutputStream fileOut = new FileOutputStream(f); 376 | ObjectOutputStream objectOut = new ObjectOutputStream(fileOut); 377 | objectOut.writeObject(data); 378 | objectOut.close(); 379 | 380 | } catch (Exception ex) { 381 | ex.printStackTrace(); 382 | } 383 | } 384 | 385 | public void applySwingValues() { 386 | data.optionsFileString = optionsFile.getAbsolutePath(); 387 | data.defaultFileChooserDirectory = DefaultFileChooserPathOptionTextField.getText(); 388 | data.VIA_Address = Integer.parseInt(VIAAddressOptionTextField.getText()); 389 | data.ACIA_Address = Integer.parseInt(ACIAAddressOptionTextField.getText()); 390 | data.GPUWidth = Integer.parseInt(GPUWidthTextField.getText()); 391 | data.GPUHeight = Integer.parseInt(GPUHeightTextField.getText()); 392 | data.GPUCols = Integer.parseInt(GPUColsTextField.getText()); 393 | data.GPURows = Integer.parseInt(GPURowsTextField.getText()); 394 | data.GPUBufferBegin = Integer.parseInt(GPUBufferAddressOptionTextField.getText()); 395 | data.GPUMode = Integer.parseInt(GPUModeOptionTextField.getText()); 396 | data.fgColor = Color.decode(ForegroundColorChooser.getText()); 397 | data.bgColor = Color.decode(BackgroundColorChooser.getText()); 398 | data.GPUBitmapPixelScale = Integer.parseInt(GPUBitmapPixelScaleTextField.getText()); 399 | data.keyboardLocation = Integer.parseInt(KeyboardLocationTextField.getText()); 400 | data.keyboardMode = Integer.parseInt(KeyboardModeOptionTextField.getText()); 401 | 402 | Bus.VIA_ADDRESS = data.VIA_Address; 403 | Bus.ACIA_ADDRESS = data.ACIA_Address; 404 | boolean gpuWasVisible = EaterEmulator.gpu.isVisible(); 405 | EaterEmulator.gpu.dispose(); 406 | 407 | data.GPUWidth = GPU.width = (data.GPUMode == 1) ? 100 : data.GPUWidth; 408 | data.GPUHeight = GPU.height = (data.GPUMode == 1) ? 75 : data.GPUHeight; 409 | data.GPUBufferBegin = GPU.VRAM_START_ADDRESS = (data.GPUMode == 1) ? 8192 : data.GPUBufferBegin; 410 | 411 | GPU.n_cols = data.GPUCols; 412 | GPU.n_rows = data.GPURows; 413 | GPU.gpuMode = data.GPUMode; 414 | GPU.GPUPixelScale = data.GPUBitmapPixelScale; 415 | 416 | EaterEmulator.gpu = new GPU(EaterEmulator.gpu.vram,gpuWasVisible); 417 | 418 | DisplayPanel.fgColor = data.fgColor; 419 | DisplayPanel.bgColor = data.bgColor; 420 | EaterEmulator.disOutput.updateDisplay(); 421 | 422 | EaterEmulator.GraphicsPanel.resetGraphics(); 423 | 424 | EaterEmulator.fc.setDirectory(data.defaultFileChooserDirectory); 425 | fc.setDirectory(data.defaultFileChooserDirectory); 426 | 427 | data.lcdBigMode = LCDModeRadioLarge.isSelected(); 428 | EaterEmulator.lcd.bigMode = data.lcdBigMode; 429 | EaterEmulator.lcd.updateMode(); 430 | 431 | data.carriageReturn = LineEndingRadioR.isSelected(); 432 | EaterEmulator.carriageReturn = data.carriageReturn; 433 | 434 | EaterEmulator.realisticKeyboard = (data.keyboardMode == 1); 435 | } 436 | 437 | private void resetSwingPositions() { 438 | openOptionsFileButton.setBounds(575,10,100,25); 439 | openOptionsFileText.setBounds(175,10,400,25); 440 | openOptionsFileLabel.setBounds(25,10,200,25); 441 | 442 | DefaultFileChooserPathOptionLabel.setBounds(125,40,175,25); 443 | DefaultFileChooserPathOptionTextField.setBounds(300,40,350,25); 444 | 445 | VIAAddressOptionLabel.setBounds(200,80,100,25); 446 | VIAAddressOptionTextField.setBounds(300,80,100,25); 447 | VIAAddressOptionHexLabel.setBounds(400,80,100,25); 448 | 449 | ACIAAddressOptionLabel.setBounds(200,110,100,25); 450 | ACIAAddressOptionTextField.setBounds(300,110,100,25); 451 | ACIAAddressOptionHexLabel.setBounds(400,110,100,25); 452 | 453 | GPUResolutionOptionLabel.setBounds(150,150,150,25); 454 | GPUWidthTextField.setBounds(300,150,50,25); 455 | GPUHeightTextField.setBounds(350,150,50,25); 456 | 457 | GPUCharGridOptionLabel.setBounds(150,190,150,25); 458 | GPUColsTextField.setBounds(300,190,50,25); 459 | GPURowsTextField.setBounds(350,190,50,25); 460 | VRAMSizeLabel.setBounds(400,(data.GPUMode == 1 || data.GPUMode == 2) ? 150 : 190,100,25); 461 | 462 | GPUBufferAddressOptionLabel.setBounds(75,230,225,25); 463 | GPUBufferAddressOptionTextField.setBounds(300,230,100,25); 464 | GPUBufferAddressOptionHexLabel.setBounds(400,230,100,25); 465 | 466 | GPUModeOptionLabel.setBounds(225,270,75,25); 467 | GPUModeOptionTextField.setBounds(300,270,25,25); 468 | 469 | GPUBitmapPixelScaleLabel.setBounds(150,310,150,25); 470 | GPUBitmapPixelScaleTextField.setBounds(300,310,25,25); 471 | 472 | VRAMRangeLabel.setBounds(175,350,300,25); 473 | 474 | GPUModeExplanationLabel.setBounds(175,370,500,100); 475 | 476 | KeyboardLocationLabel.setBounds(120,470,185,25); 477 | KeyboardLocationTextField.setBounds(300,470,100,25); 478 | KeyboardLocationHexLabel.setBounds(400,470,100,25); 479 | 480 | KeyboardModeOptionLabel.setBounds(180,500,175,25); 481 | KeyboardModeOptionTextField.setBounds(300,500,25,25); 482 | KeyboardModeExplanationLabel.setBounds(175,520,500,60); 483 | 484 | LineEndingLabel.setBounds(225,580,75,25); 485 | LineEndingRadioR.setBounds(300,580,100,25); 486 | LineEndingRadioN.setBounds(400,580,100,25); 487 | 488 | LCDModeLabel.setBounds(225,610,75,25); 489 | LCDModeRadioSmall.setBounds(300,610,100,25); 490 | LCDModeRadioLarge.setBounds(400,610,100,25); 491 | 492 | ForegroundColorLabel.setBounds(175,640,125,25); 493 | ForegroundColorChooser.setBounds(300,640,100,25); 494 | 495 | BackgroundColorLabel.setBounds(175,670,125,25); 496 | BackgroundColorChooser.setBounds(300,670,100,25); 497 | 498 | applyOptionsButton.setBounds(200,720,150,25); 499 | saveOptionsButton.setBounds(350,720,150,25); 500 | } 501 | } 502 | -------------------------------------------------------------------------------- /src/Point.java: -------------------------------------------------------------------------------- 1 | public class Point { 2 | double x; 3 | double y; 4 | 5 | public Point(double x, double y) { 6 | this.x = x; 7 | this.y = y; 8 | } 9 | 10 | public double getX() { 11 | return x; 12 | } 13 | public void setX(double x) { 14 | this.x = x; 15 | } 16 | public double getY() { 17 | return y; 18 | } 19 | public void setY(double y) { 20 | this.y = y; 21 | } 22 | 23 | public Point multiply(Point other) { 24 | this.x*=other.getX(); 25 | this.y*=other.getY(); 26 | return this; 27 | } 28 | 29 | @Override 30 | public String toString() { 31 | return "("+this.x+","+this.y+")"; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/RAM.java: -------------------------------------------------------------------------------- 1 | public class RAM { 2 | private byte[] array; 3 | public String RAMString = ""; 4 | 5 | public RAM() { 6 | array = new byte[0x8000]; 7 | for (int i = 0; i<0x8000; i++) { 8 | array[i] = (byte)0x00; 9 | } 10 | RAMString = this.toString(8, true); 11 | } 12 | 13 | public RAM(byte[] theArray) { 14 | array = theArray; 15 | RAMString = this.toString(8, true); 16 | } 17 | 18 | public byte[] getRAMArray() { 19 | return array; 20 | } 21 | 22 | public void setRAMArray(byte[] array) { 23 | this.array = array; 24 | RAMString = this.toString(8, true); 25 | } 26 | 27 | public byte read(short address) { 28 | return array[Short.toUnsignedInt(address)]; 29 | } 30 | 31 | public void write(short address, byte data) { 32 | array[Short.toUnsignedInt(address)] = data; 33 | RAMString = this.toString(8, true); 34 | } 35 | 36 | public String toString(int bytesPerLine, boolean addresses) { 37 | StringBuilder sb = new StringBuilder(); 38 | 39 | if (addresses) 40 | sb.append("0000: "); 41 | 42 | for (int i = 1; i <= array.length; i++) { 43 | if ((i%bytesPerLine != 0) || (i == 0)) { 44 | sb.append(ROMLoader.byteToHexString(array[i-1])+" "); 45 | } else { 46 | String zeroes = "0000"; 47 | sb.append(ROMLoader.byteToHexString(array[i-1])+"\n"); 48 | if (addresses) 49 | sb.append(zeroes.substring(0, Math.max(0, 4-Integer.toHexString(i).length()))+Integer.toHexString(i)+": "); 50 | } 51 | } 52 | 53 | return sb.toString(); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/ROM.java: -------------------------------------------------------------------------------- 1 | public class ROM { 2 | private byte[] array; 3 | public String ROMString = ""; 4 | 5 | public ROM() { 6 | array = new byte[0x8000]; 7 | for (int i = 0; i<0x8000; i++) { 8 | array[i] = (byte)0x00; 9 | } 10 | ROMString = this.toStringWithOffset(8,0x8000,true); 11 | } 12 | 13 | public ROM(byte[] theArray) { 14 | array = theArray; 15 | ROMString = this.toStringWithOffset(8,0x8000,true); 16 | } 17 | 18 | public byte[] getROMArray() { 19 | return array; 20 | } 21 | 22 | public void setROMArray(byte[] array) { 23 | this.array = array; 24 | ROMString = this.toStringWithOffset(8,0x8000,true); 25 | } 26 | 27 | public byte read(short address) { 28 | return array[Short.toUnsignedInt(address)]; 29 | } 30 | 31 | public String toString(int bytesPerLine, boolean addresses) { 32 | StringBuilder sb = new StringBuilder(); 33 | 34 | if (addresses) 35 | sb.append("0000: "); 36 | 37 | for (int i = 1; i <= array.length; i++) { 38 | if ((i%bytesPerLine != 0) || (i == 0)) { 39 | sb.append(ROMLoader.byteToHexString(array[i-1])+" "); 40 | } else { 41 | String zeroes = "0000"; 42 | sb.append(ROMLoader.byteToHexString(array[i-1])+"\n"); 43 | if (addresses) 44 | sb.append(zeroes.substring(0, 4-Integer.toHexString(i).length())+Integer.toHexString(i)+": "); 45 | } 46 | } 47 | 48 | return sb.toString(); 49 | } 50 | 51 | public String toStringWithOffset(int bytesPerLine, int addressOffset, boolean addresses) { 52 | StringBuilder sb = new StringBuilder(); 53 | String zeroes = "0000"; 54 | 55 | if (addresses) 56 | sb.append(zeroes.substring(0, 4-Integer.toHexString(0+addressOffset).length())+Integer.toHexString(0+addressOffset)+": "); 57 | 58 | for (int i = 1; i <= array.length; i++) { 59 | if ((i%bytesPerLine != 0) || (i == 0)) { 60 | sb.append(ROMLoader.byteToHexString(array[i-1])+" "); 61 | } else { 62 | sb.append(ROMLoader.byteToHexString(array[i-1])+"\n"); 63 | if (addresses) 64 | sb.append(Integer.toHexString(i+addressOffset)+": "); 65 | } 66 | } 67 | 68 | return sb.toString(); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/ROMLoader.java: -------------------------------------------------------------------------------- 1 | import java.io.*; 2 | import java.util.ArrayList; 3 | 4 | public class ROMLoader { 5 | static ArrayList ROM = new ArrayList(); 6 | 7 | public static byte[] readROM(File file) { 8 | ROM = new ArrayList(); 9 | 10 | InputStream inputStream; 11 | try { 12 | inputStream = new FileInputStream(file); 13 | int byteRead; 14 | 15 | while ((byteRead = inputStream.read()) != -1) { 16 | ROM.add((byte)byteRead); 17 | } 18 | 19 | } catch (IOException e) { 20 | e.printStackTrace(); 21 | } 22 | 23 | byte[] ROMArray = new byte[ROM.size()]; 24 | 25 | int counter = 0; 26 | for (Byte b : ROM) { 27 | ROMArray[counter] = b.byteValue(); 28 | counter++; 29 | } 30 | 31 | return ROMArray; 32 | } 33 | 34 | public static String ROMString(byte[] array,int bytesPerLine,boolean addresses) { 35 | StringBuilder sb = new StringBuilder(); 36 | 37 | if (addresses) 38 | sb.append("0000: "); 39 | 40 | for (int i = 1; i <= array.length; i++) { 41 | if ((i%bytesPerLine != 0) || (i == 0)) { 42 | sb.append(byteToHexString(array[i-1])+" "); 43 | } else { 44 | String zeroes = "0000"; 45 | sb.append(byteToHexString(array[i-1])+"\n"); 46 | if (addresses) 47 | sb.append(zeroes.substring(0, 4-Integer.toHexString(i).length())+Integer.toHexString(i)+": "); 48 | } 49 | } 50 | 51 | return sb.toString(); 52 | } 53 | 54 | public static String ROMString(byte[] array) { 55 | StringBuilder sb = new StringBuilder(); 56 | int bytesPerLine = 16; 57 | 58 | for (int i = 1; i <= array.length; i++) { 59 | if ((i%bytesPerLine != 0) || (i == 0)) { 60 | sb.append(byteToHexString(array[i-1])+" "); 61 | } else { 62 | sb.append(byteToHexString(array[i-1])+"\n"); 63 | } 64 | } 65 | 66 | return sb.toString(); 67 | } 68 | 69 | public static String byteToHexString(byte b) { 70 | if (Byte.toUnsignedInt(b) < 16) 71 | return "0"+Integer.toHexString(Byte.toUnsignedInt(b)).toUpperCase(); 72 | return Integer.toHexString(Byte.toUnsignedInt(b)).toUpperCase(); 73 | } 74 | 75 | public static String padStringWithZeroes(String s,int padLength) { 76 | char[] pads = new char[padLength-s.length()]; 77 | for (int i = 0; i= ' ' && c <= '~'; //normal ascii range 115 | } 116 | 117 | private char convertChar(char keyChar) { 118 | if (keyChar == '\n' && EaterEmulator.carriageReturn) return '\r'; 119 | return keyChar; 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/VIA.java: -------------------------------------------------------------------------------- 1 | public class VIA { 2 | byte PORTA = 0x00; 3 | byte PORTB = 0x00; 4 | byte DDRA = 0x00; 5 | byte DDRB = 0x00; 6 | byte PCR = 0x00; 7 | byte IFR = 0x00; 8 | byte IER = 0x00; 9 | 10 | public byte read(short address) { 11 | switch (Short.toUnsignedInt(address)-Bus.VIA_ADDRESS) { 12 | case 0x000: 13 | IFR &= (byte)(0b01100111); 14 | PORTB = EaterEmulator.lcd.read((PORTA & 0x20) != 0); 15 | return PORTB; 16 | case 0x001: 17 | IFR &= (byte)(0b01111100); 18 | byte value = 0; 19 | if(!DisplayPanel.keys.isEmpty() && EaterEmulator.realisticKeyboard) { 20 | value = DisplayPanel.keys.getFirst(); 21 | DisplayPanel.keys.removeFirst(); 22 | } 23 | return value; 24 | case 0x002: 25 | return 0; 26 | case 0x003: 27 | return 0; 28 | case 0x00C: 29 | return PCR; 30 | case 0x00D: 31 | return IFR; 32 | case 0x00E: 33 | return IER; 34 | } 35 | return 0; 36 | } 37 | 38 | public void write(short address, byte data) { 39 | switch (Short.toUnsignedInt(address)-Bus.VIA_ADDRESS) { 40 | case 0x000: 41 | PORTB = data; 42 | if ((PORTA&0x80)==0x80 && (PORTA&0x40)==0x00) { 43 | EaterEmulator.lcd.write((PORTA&0x20)==0x20, (byte)(PORTB&DDRB)); 44 | } 45 | break; 46 | case 0x001: 47 | PORTA = data; 48 | if ((PORTA&0x80)==0x80 && (PORTA&0x40)==0x00) { 49 | EaterEmulator.lcd.write((PORTA&0x20)==0x20, (byte)(PORTB&DDRB)); 50 | } 51 | break; 52 | case 0x002: 53 | DDRB = data; 54 | break; 55 | case 0x003: 56 | DDRA = data; 57 | break; 58 | case 0x00C: 59 | PCR = data; 60 | break; 61 | case 0x00D: 62 | IFR = data; 63 | break; 64 | case 0x00E: 65 | IER = data; 66 | break; 67 | } 68 | if(!EaterEmulator.cpu.interruptRequested && !DisplayPanel.keys.isEmpty() && EaterEmulator.realisticKeyboard) { //some additional keys, like for release where it sends 2, or maybe two presses really fast 69 | CA1(); 70 | } 71 | } 72 | 73 | public void CA1() { 74 | if ((IER & (byte)(0b00000010)) == 0b00000010) { 75 | IFR |= (byte)(0b10000010); 76 | EaterEmulator.cpu.interruptRequested = true; 77 | } 78 | } 79 | 80 | public void CA2() { 81 | if ((IER & (byte)(0b00000001)) == 0b00000001) { 82 | IFR |= (byte)(0b10000001); 83 | EaterEmulator.cpu.interruptRequested = true; 84 | } 85 | } 86 | 87 | public void CB1() { 88 | if ((IER & (byte)(0b00010000)) == 0b00010000) { 89 | IFR |= (byte)(0b10010000); 90 | EaterEmulator.cpu.interruptRequested = true; 91 | } 92 | } 93 | 94 | public void CB2() { 95 | if ((IER & (byte)(0b00001000)) == 0b00001000) { 96 | IFR |= (byte)(0b10001000); 97 | EaterEmulator.cpu.interruptRequested = true; 98 | } 99 | } 100 | } 101 | --------------------------------------------------------------------------------