├── bin └── .gitkeep ├── docs ├── .gitkeep ├── mos-6502.png └── pong-window.gif ├── src ├── .gitkeep ├── bus.cpp ├── main.cpp ├── iochip.cpp └── cpu.cpp ├── examples ├── .gitkeep ├── audio_beep.asm ├── vram_simple_copy.asm ├── audio_control.asm ├── vram_clock.asm ├── vram_draw_rectangles.asm ├── vram_keyboard.asm └── c │ ├── pong_oc.asm │ ├── pong_oc_annotated_2.asm │ ├── pong_oc_annotated.asm │ ├── pong.asm │ └── pong.cpp ├── include ├── .gitkeep ├── rommodule.h ├── rammodule.h ├── busdevice.h ├── bus.h ├── iochip.h └── cpu.h ├── .gitignore ├── experiments ├── sfml_window_test.cpp ├── sfml_audio_test.cpp └── locks.cpp ├── LICENSE.md ├── README.md ├── Makefile └── .clang-format /bin/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /examples/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /include/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | *.swo 3 | .DS_Store 4 | bin/ 5 | build/ 6 | -------------------------------------------------------------------------------- /docs/mos-6502.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KCreate/mos6502/HEAD/docs/mos-6502.png -------------------------------------------------------------------------------- /docs/pong-window.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KCreate/mos6502/HEAD/docs/pong-window.gif -------------------------------------------------------------------------------- /examples/audio_beep.asm: -------------------------------------------------------------------------------- 1 | ; this program beeps audio channel 1 2 | 3 | ; some commonly used addresses 4 | .def ADDR_CLOCK1 0x4906 5 | .def ADDR_AUDIO1 0x4908 6 | 7 | .RST 8 | 9 | ; setup the audio channel 10 | lda #$D1 11 | sta ADDR_AUDIO1 12 | 13 | ; setup toggle byte 14 | lda #$18 15 | sta TOGGLE 16 | 17 | ; setup the clock 18 | lda #$16 19 | sta ADDR_CLOCK1 20 | 21 | ; loop 22 | .LOOP 23 | nop 24 | jmp .LOOP 25 | 26 | .IRQ 27 | lda ADDR_AUDIO1 28 | ldx TOGGLE 29 | sta TOGGLE 30 | txa 31 | sta ADDR_AUDIO1 32 | rti 33 | 34 | ; used to store the last value inside audio channel 1 35 | .org 0x00 36 | .BYTE TOGGLE 37 | -------------------------------------------------------------------------------- /examples/vram_simple_copy.asm: -------------------------------------------------------------------------------- 1 | ; This is a test program for the 6502 Emulator. 2 | ; It will copy the value 0x1C to every address 3 | ; from VRAM - VRAM+0xFF 4 | 5 | .def VRAM 0x4000 6 | .def ILLEGAL_OPCODE 0xFF 7 | 8 | .RST 9 | ldx #$00 ; initialize counter variable 10 | lda #$1C ; initialize template variable 11 | 12 | .HEAD 13 | sta VRAM, x ; copy the template byte to VRAM + counter 14 | cpx #$FF ; if the counter variable has reached 0xFF 15 | beq .END ; we jump to the end 16 | inx ; increment the counter 17 | jmp .HEAD ; jump back to the loop condition 18 | .END 19 | nop 20 | jmp .END ; endless loop for testing purposes 21 | -------------------------------------------------------------------------------- /experiments/sfml_window_test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | int main() { 7 | sf::Window window(sf::VideoMode(800, 600), "My window"); 8 | window.setFramerateLimit(60); 9 | window.setPosition(sf::Vector2i(10, 50)); 10 | window.setSize(sf::Vector2u(640, 480)); 11 | window.setTitle("SFML window"); 12 | 13 | uint64_t eid = 0; 14 | while (window.isOpen()) { 15 | sf::Event event; 16 | while (window.waitEvent(event)) { 17 | std::cout << "got event: " << eid << std::endl; 18 | eid += 1; 19 | if (event.type == sf::Event::Closed) { 20 | window.close(); 21 | } 22 | } 23 | } 24 | 25 | return 0; 26 | } 27 | -------------------------------------------------------------------------------- /examples/audio_control.asm: -------------------------------------------------------------------------------- 1 | ; this example program plays a sound for 1 second at a time 2 | ; after each audio pulse it pauses for a second 3 | ; after the pause the audio will change it's wave form 4 | 5 | ; some commonly used addresses 6 | .def ADDR_CLOCK1 0x4906 7 | .def ADDR_AUDIO1 0x4908 8 | 9 | .RST 10 | 11 | ; setup the audio channel 12 | lda #$C8 13 | sta ADDR_AUDIO1 14 | 15 | ; setup the 1 second clock 16 | lda #$64 17 | sta ADDR_CLOCK1 18 | 19 | ; loop 20 | .LOOP 21 | nop 22 | jmp .LOOP 23 | 24 | .IRQ 25 | 26 | ; change the wave function of the audio channel 27 | ; 28 | ; A <- audio1 29 | ; X <- A 30 | ; A <- A & 0xCF 31 | ; TMP <- A 32 | ; A <- X 33 | ; A <- A + 0x10 34 | ; A <- A & 0x30 35 | ; A <- A + TMP 36 | ; audio1 <- A 37 | lda ADDR_AUDIO1 38 | tax 39 | and #$CF 40 | sta TMP 41 | txa 42 | adc #$10 43 | and #$30 44 | adc TMP 45 | sta ADDR_AUDIO1 46 | rti 47 | 48 | ; used to store temporary results when changing wave function 49 | .org 0x00 50 | .BYTE TMP 51 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2018 Leonard Schuetz 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /experiments/sfml_audio_test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | const unsigned SAMPLES = 1000; 11 | const unsigned SAMPLE_RATE = 44100; 12 | const unsigned AMPLITUDE = 30000; 13 | const double TWO_PI = 6.28318; 14 | const double increment = 0.01; 15 | 16 | int main() { 17 | // The container of our raw data 18 | sf::Int16 raw[SAMPLES]; 19 | 20 | // Create the data for the sine wave 21 | double x = 0; 22 | for (unsigned i = 0; i < SAMPLES; i++) { 23 | raw[i] = AMPLITUDE * (sin(x * TWO_PI) >= 0.0 ? 1 : 0.5); 24 | x += increment; 25 | } 26 | 27 | // Load the data into the sound buffer 28 | sf::SoundBuffer Buffer; 29 | Buffer.loadFromSamples(raw, SAMPLES, 1, SAMPLE_RATE); 30 | 31 | // Configure the sound and play 32 | sf::Sound Sound; 33 | Sound.setBuffer(Buffer); 34 | Sound.setLoop(true); 35 | Sound.setPitch(0.2); 36 | Sound.play(); 37 | 38 | for (double p = 0.2; p < 1.4; p += 0.1) { 39 | std::this_thread::sleep_for(std::chrono::milliseconds(500)); 40 | Sound.setPitch(p); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![MOS-6502 Emulator](docs/mos-6502.png) 2 | 3 | # MOS-6502 Emulator 4 | 5 | This is a complete emulator of the original MOS Technology 6502 microcontroller from 1975. 6 | It is written in C++. 7 | 8 | The emulator works by providing a shared memory bus, to which one can connect devices. 9 | Each device is allocated an address range, in which it can provide its functionality. 10 | This allows the program to change audio-output and draw graphics by simply writing a value to a specific address. 11 | 12 | Below you can see the virtual monitor displaying a pong game. 13 | [The game is implemented completely in the 14 | 6502-Assembly language.](https://github.com/KCreate/mos6502/blob/master/examples/c/pong.asm) 15 | 16 | Pong Game 17 | 18 | # Custom modifications 19 | 20 | Since the original 6502 didn't use all available opcodes, a custom one was added. 21 | You can see a list of unused 6502 opcodes [here](http://www.oxyron.de/html/opcodes02.html). 22 | 23 | Instead of halting the CPU, it now waits until the next interrupt happens. 24 | 25 | - `0x02 - WAI` Same behaviour as on the WDC 65C02. 26 | 27 | # Contributors 28 | 29 | - Leonard Schütz, Lead Developer [@KCreate](https://github.com/KCreate) 30 | 31 | # License 32 | 33 | [MIT License](LICENSE.md) 34 | -------------------------------------------------------------------------------- /experiments/locks.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | using namespace std; 8 | 9 | std::condition_variable cv; 10 | std::mutex cv_m; 11 | 12 | std::atomic shutdown; 13 | 14 | void waits() { 15 | while (!shutdown) { 16 | std::unique_lock lk(cv_m); 17 | std::cerr << "Waiting...\n"; 18 | cv.wait(lk); 19 | std::cerr << "Activating...\n\n"; 20 | } 21 | 22 | std::cerr << "Ended waits\n"; 23 | } 24 | 25 | void signals() { 26 | std::this_thread::sleep_for(std::chrono::seconds(1)); 27 | std::cerr << "Notifying...\n"; 28 | cv.notify_all(); 29 | 30 | std::this_thread::sleep_for(std::chrono::seconds(1)); 31 | std::cerr << "Notifying...\n"; 32 | cv.notify_all(); 33 | 34 | std::this_thread::sleep_for(std::chrono::seconds(1)); 35 | std::cerr << "Notifying...\n"; 36 | cv.notify_all(); 37 | 38 | std::this_thread::sleep_for(std::chrono::seconds(1)); 39 | std::cerr << "Notifying...\n"; 40 | cv.notify_all(); 41 | 42 | std::this_thread::sleep_for(std::chrono::seconds(1)); 43 | shutdown = true; 44 | std::cerr << "Shutting down...\n"; 45 | cv.notify_all(); 46 | } 47 | 48 | int main() { 49 | shutdown = false; 50 | 51 | std::thread t1(waits), t2(signals); 52 | t1.join(); 53 | t2.join(); 54 | } 55 | -------------------------------------------------------------------------------- /examples/vram_clock.asm: -------------------------------------------------------------------------------- 1 | ; this example program draws a rectangle on the screen 2 | ; it configures clock1 to pulse every second, moving the 3 | ; rectangle one position to the right 4 | 5 | ; some commonly used addresses 6 | .def ADDR_VRAM 0x400 7 | .def ADDR_CLOCK1 0x906 8 | .def ADDR_DRAW_METHOD 0x90B 9 | .def ADDR_DRAW_ARG1 0x90C 10 | .def ADDR_DRAW_ARG2 0x90D 11 | .def ADDR_DRAW_ARG3 0x90E 12 | .def ADDR_DRAW_ARG4 0x90F 13 | 14 | ; method code of the draw rectangle IOChip intrinsic 15 | .def DRAW_RECTANGLE 0x00 16 | .def BRUSH_SET_BODY 0x80 17 | .def BRUSH_SET_OUTLINE 0x81 18 | 19 | ; color codes 20 | .def RED #$E0 21 | .def BLUE #$03 22 | 23 | ; load at reset entry 24 | .RST 25 | 26 | ; setup the clock to pulse every 1000 milliseconds 27 | lda #$C8 28 | sta ADDR_CLOCK1 29 | 30 | ; set the brush color to red 31 | lda RED 32 | sta ADDR_DRAW_ARG1 33 | lda BRUSH_SET_BODY 34 | sta ADDR_DRAW_METHOD 35 | 36 | ; set the outline color to blue 37 | lda BLUE 38 | sta ADDR_DRAW_ARG1 39 | lda BRUSH_SET_BODY 40 | sta ADDR_DRAW_METHOD 41 | 42 | ; Draw the rectangle 43 | .DRAW 44 | stx ADDR_DRAW_ARG1 ; x coordinate 45 | lda #$08 ; y coordinate 46 | sta ADDR_DRAW_ARG2 47 | lda #$10 ; width 48 | sta ADDR_DRAW_ARG3 49 | lda #$14 ; height 50 | sta ADDR_DRAW_ARG4 51 | lda DRAW_RECTANGLE 52 | sta ADDR_DRAW_METHOD 53 | jmp .DRAW 54 | 55 | ; increase the X register each time the clock pulses 56 | .IRQ 57 | inx 58 | rti 59 | -------------------------------------------------------------------------------- /examples/vram_draw_rectangles.asm: -------------------------------------------------------------------------------- 1 | ; this example program draws two rectangles on the screen, 2 | ; one colored red and the other colored green 3 | 4 | ; some commonly used addresses 5 | .def ADDR_VRAM 0x400 6 | .def ADDR_DRAW_METHOD 0x90B 7 | .def ADDR_DRAW_ARG1 0x90C 8 | .def ADDR_DRAW_ARG2 0x90D 9 | .def ADDR_DRAW_ARG3 0x90E 10 | .def ADDR_DRAW_ARG4 0x90F 11 | 12 | ; method code of the draw rectangle IOChip intrinsic 13 | .def DRAW_RECTANGLE 0x00 14 | .def BRUSH_SET_BODY 0x80 15 | .def BRUSH_SET_OUTLINE 0x81 16 | 17 | ; color codes 18 | .def RED #$0xE0 19 | .def GREEN #$0x1C 20 | 21 | ; load at reset entry 22 | .RST 23 | 24 | ; set the brush color to red 25 | lda RED 26 | sta ADDR_DRAW_ARG1 27 | lda BRUSH_SET_BODY 28 | sta ADDR_DRAW_METHOD 29 | 30 | ; draw the rectangle 31 | lda #$10 ; x coordinate 32 | sta ADDR_DRAW_ARG1 33 | lda #$08 ; y coordinate 34 | sta ADDR_DRAW_ARG2 35 | lda #$10 ; width 36 | sta ADDR_DRAW_ARG3 37 | lda #$26 ; height 38 | sta ADDR_DRAW_ARG4 39 | lda DRAW_RECTANGLE 40 | sta ADDR_DRAW_METHOD 41 | 42 | ; set the brush color to green 43 | lda GREEN 44 | sta ADDR_DRAW_ARG1 45 | lda BRUSH_SET_BODY 46 | sta ADDR_DRAW_METHOD 47 | 48 | ; draw the rectangle 49 | lda #$20 ; x coordinate 50 | sta ADDR_DRAW_ARG1 51 | lda #$08 ; y coordinate 52 | sta ADDR_DRAW_ARG2 53 | lda #$10 ; width 54 | sta ADDR_DRAW_ARG3 55 | lda #$26 ; height 56 | sta ADDR_DRAW_ARG4 57 | lda DRAW_RECTANGLE 58 | sta ADDR_DRAW_METHOD 59 | 60 | .END 61 | nop 62 | jmp .END 63 | -------------------------------------------------------------------------------- /include/rommodule.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the MOS 6502 Emulator 3 | * (https://github.com/KCreate/mos6502) 4 | * 5 | * MIT License 6 | * 7 | * Copyright (c) 2017 - 2018 Leonard Schütz 8 | * 9 | * Permission is hereby granted, free of charge, to any person obtaining a copy 10 | * of this software and associated documentation files (the "Software"), to deal 11 | * in the Software without restriction, including without limitation the rights 12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | * copies of the Software, and to permit persons to whom the Software is 14 | * furnished to do so, subject to the following conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be included in 17 | * all copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 25 | * SOFTWARE. 26 | */ 27 | 28 | #include 29 | #include 30 | 31 | #pragma once 32 | 33 | namespace M6502 { 34 | 35 | // Regular readonly memory of a given size 36 | template 37 | class ROMModule : public ReadOnlyDevice { 38 | public: 39 | ROMModule(uint16_t maddr) : ReadOnlyDevice(maddr) { 40 | std::memset(this->buffer, 0xFF, C); 41 | } 42 | 43 | uint8_t read(uint16_t address) { 44 | return this->buffer[address]; 45 | } 46 | 47 | inline uint8_t* get_buffer() { 48 | return this->buffer; 49 | } 50 | 51 | private: 52 | uint8_t buffer[C]; 53 | size_t capacity = C; 54 | }; 55 | } // namespace M6502 56 | -------------------------------------------------------------------------------- /include/rammodule.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the MOS 6502 Emulator 3 | * (https://github.com/KCreate/mos6502) 4 | * 5 | * MIT License 6 | * 7 | * Copyright (c) 2017 - 2018 Leonard Schütz 8 | * 9 | * Permission is hereby granted, free of charge, to any person obtaining a copy 10 | * of this software and associated documentation files (the "Software"), to deal 11 | * in the Software without restriction, including without limitation the rights 12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | * copies of the Software, and to permit persons to whom the Software is 14 | * furnished to do so, subject to the following conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be included in 17 | * all copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 25 | * SOFTWARE. 26 | */ 27 | 28 | #include 29 | #include 30 | 31 | #pragma once 32 | 33 | namespace M6502 { 34 | 35 | // Regular read/write memory of a given size 36 | template 37 | class RAMModule : public BusDevice { 38 | public: 39 | RAMModule(uint16_t maddr) : BusDevice(maddr) { 40 | std::memset(this->buffer, 0xFF, C); 41 | } 42 | 43 | uint8_t read(uint16_t address) { 44 | return this->buffer[address]; 45 | } 46 | 47 | void write(uint16_t address, uint8_t value) { 48 | this->buffer[address] = value; 49 | } 50 | 51 | private: 52 | uint8_t buffer[C]; 53 | size_t capacity = C; 54 | }; 55 | } // namespace M6502 56 | -------------------------------------------------------------------------------- /examples/vram_keyboard.asm: -------------------------------------------------------------------------------- 1 | ; this example program draws a rectangle on the screen 2 | ; pressing the a key on the keyboard will move the rectangle to the left 3 | ; pressing the b key on the keyboard will move the rectangle to the right 4 | 5 | ; some commonly used addresses 6 | .def ADDR_VRAM 0x4000 7 | .def ADDR_EVENT_TYPE 0x4903 8 | .def ADDR_KEYBOARD_KEYCODE 0x4904 9 | .def ADDR_DRAW_METHOD 0x490B 10 | .def ADDR_DRAW_ARG1 0x490C 11 | .def ADDR_DRAW_ARG2 0x490D 12 | .def ADDR_DRAW_ARG3 0x490E 13 | .def ADDR_DRAW_ARG4 0x490F 14 | 15 | ; method code of the draw rectangle IOChip intrinsic 16 | .def DRAW_RECTANGLE 0x00 17 | .def BRUSH_SET_BODY 0x80 18 | .def BRUSH_SET_OUTLINE 0x81 19 | 20 | ; color codes 21 | .def RED #$E0 22 | .def BLUE #$03 23 | 24 | ; event types 25 | .def EVENT_KEYDOWN 0x01 26 | 27 | ; keyboard keycodes 28 | .def KEY_W 0x16 29 | .def KEY_A 0x00 30 | .def KEY_S 0x12 31 | .def KEY_D 0x03 32 | 33 | ; load at reset entry 34 | .RST 35 | 36 | ; set the body color to red 37 | lda RED 38 | sta ADDR_DRAW_ARG1 39 | lda BRUSH_SET_BODY 40 | sta ADDR_DRAW_METHOD 41 | 42 | ; set the outline color to blue 43 | lda BLUE 44 | sta ADDR_DRAW_ARG1 45 | lda BRUSH_SET_OUTLINE 46 | sta_ADDR_DRAW_METHOD 47 | 48 | ; Drawing loop 49 | .DRAW 50 | lda #$10 ; width / height 51 | sta ADDR_DRAW_ARG3 52 | sta ADDR_DRAW_ARG4 53 | lda DRAW_RECTANGLE 54 | .DRAWLOOP 55 | stx ADDR_DRAW_ARG1 ; x coordinate 56 | sty ADDR_DRAW_ARG2 ; y coordinate 57 | sta ADDR_DRAW_METHOD 58 | jmp .DRAWLOOP 59 | 60 | ; Move the rectangle with the WASD keys 61 | .IRQ 62 | 63 | ; Backup A register 64 | PHA 65 | 66 | ; Check if this is a keyboard event 67 | lda ADDR_EVENT_TYPE 68 | cmp EVENT_KEYDOWN 69 | bne .RESTORE_AND_EXIT 70 | 71 | ; Check if either the A or D key were pressed 72 | lda ADDR_KEYBOARD_KEYCODE 73 | cmp KEY_W 74 | beq .HANDLE_W_KEY 75 | cmp KEY_A 76 | beq .HANDLE_A_KEY 77 | cmp KEY_S 78 | beq .HANDLE_D_KEY 79 | cmp KEY_D 80 | beq .HANDLE_D_KEY 81 | 82 | .RESTORE_AND_EXIT 83 | PLA 84 | rti 85 | 86 | .HANDLE_W_KEY 87 | PLA 88 | dey 89 | rti 90 | 91 | .HANDLE_A_KEY 92 | PLA 93 | dex 94 | rti 95 | 96 | .HANDLE_S_KEY 97 | PLA 98 | iny 99 | rti 100 | 101 | .HANDLE_D_KEY 102 | PLA 103 | inx 104 | rti 105 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CC := clang 2 | OPT := -O0 3 | OPTPROD := -O3 -Ofast 4 | SRCDIR := src 5 | BUILDDIR := build 6 | INCLUDEDIR := include 7 | TARGET := bin/cpu 8 | SRCEXT := cpp 9 | HEADEREXT := h 10 | HEADERS := $(shell find $(INCLUDEDIR) -type f -name *.$(HEADEREXT)) 11 | SOURCES := $(shell find $(SRCDIR) -type f -name *.$(SRCEXT)) 12 | OBJECTS := $(patsubst $(SRCDIR)/%,$(BUILDDIR)/$(basename $(notdir %)),$(SOURCES:.$(SRCEXT)=.o)) 13 | CPPSTD := c++17 14 | CFLAGS := -std=$(CPPSTD) \ 15 | -g \ 16 | -Wall \ 17 | -Wextra \ 18 | -Werror \ 19 | -Wno-unused-private-field \ 20 | -ferror-limit=50 \ 21 | -ffast-math 22 | CFLAGSPROD := -std=$(CPPSTD) \ 23 | -Wall \ 24 | -Wextra \ 25 | -Werror \ 26 | -Wno-unused-private-field \ 27 | -ferror-limit=1 \ 28 | -flto \ 29 | -ffast-math 30 | LFLAGS_OSX := -lm \ 31 | -F/Library/Frameworks \ 32 | -framework sfml-window \ 33 | -framework sfml-audio \ 34 | -framework sfml-graphics \ 35 | -framework sfml-system 36 | LFLAGS_LINUX := -lX11 -lm -lpthread -lsfml -lsfml-window -lsfml-audio -lsfml-graphics -lsfml-system 37 | INC := -I libs -I $(INCLUDEDIR) -I/usr/local/Cellar/sfml/2.5.1/include -F/Library/Frameworks 38 | LIB := -lstdc++ 39 | OS = $(shell uname -s) 40 | 41 | ifeq ("$(OS)","Linux") 42 | LFLAGS = $(LFLAGS_LINUX) 43 | CFLAGS += -D LINUX 44 | CFLAGS += -pthread 45 | else 46 | LFLAGS = $(LFLAGS_OSX) 47 | CFLAGS += -D OSX 48 | endif 49 | 50 | $(TARGET): $(OBJECTS) 51 | $(call colorecho, " Linking...", 2) 52 | @$(CC) $(OBJECTS) $(CFLAGS) $(OPT) $(LIB) $(LFLAGS) -o $(TARGET) 53 | $(call colorecho, " Built executable $(TARGET)", 2) 54 | 55 | $(BUILDDIR)/%.o: $(SRCDIR)/%.$(SRCEXT) 56 | @mkdir -p $(dir $@) 57 | $(call colorecho, " Building $@", 3) 58 | @$(CC) $(CFLAGS) $(OPT) $(INC) -c -o $@ $< 59 | $(call colorecho, " Built $@", 2) 60 | 61 | production: 62 | $(call colorecho, " Building production binary $(TARGET)", 2) 63 | @$(CC) $(SOURCES) $(CFLAGSPROD) $(OPTPROD) $(INC) $(LIB) $(LFLAGS) -o $(TARGET) 64 | 65 | clean: 66 | $(call colorecho, " Cleaning...", 2) 67 | @rm -rf $(BUILDDIR) $(TARGET) 68 | 69 | rebuild: 70 | @make clean 71 | @make -j 72 | 73 | format: 74 | $(call colorecho, " Formatting...", 2) 75 | @clang-format -i $(SOURCES) $(HEADERS) -style=file 76 | 77 | .PHONY: whole clean rebuild format 78 | 79 | # Create colored output 80 | define colorecho 81 | @tput setaf $2 82 | @echo $1 83 | @tput sgr0 84 | endef 85 | -------------------------------------------------------------------------------- /include/busdevice.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the MOS 6502 Emulator 3 | * (https://github.com/KCreate/mos6502) 4 | * 5 | * MIT License 6 | * 7 | * Copyright (c) 2017 - 2018 Leonard Schütz 8 | * 9 | * Permission is hereby granted, free of charge, to any person obtaining a copy 10 | * of this software and associated documentation files (the "Software"), to deal 11 | * in the Software without restriction, including without limitation the rights 12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | * copies of the Software, and to permit persons to whom the Software is 14 | * furnished to do so, subject to the following conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be included in 17 | * all copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 25 | * SOFTWARE. 26 | */ 27 | 28 | #include 29 | #include // for std::function 30 | #include 31 | 32 | #pragma once 33 | 34 | namespace M6502 { 35 | 36 | using BusRead = std::function; 37 | using BusWrite = std::function; 38 | 39 | class Bus; // forward declaration 40 | 41 | // Abstraction of a device attached to the bus 42 | class BusDevice { 43 | public: 44 | BusDevice(uint16_t maddr) : mapped_address(maddr) { 45 | } 46 | 47 | // Read and write access to the device 48 | virtual uint8_t read(uint16_t address) = 0; 49 | virtual void write(uint16_t address, uint8_t value) = 0; 50 | 51 | // The address at which this device was mapped into memory 52 | uint16_t mapped_address; 53 | Bus* bus; 54 | }; 55 | 56 | // A device which can only be read from 57 | class ReadOnlyDevice : public BusDevice { 58 | public: 59 | using BusDevice::BusDevice; 60 | 61 | inline void write(uint16_t, uint8_t) { 62 | // do nothing 63 | } 64 | }; 65 | 66 | // A device which can only be written to 67 | class WriteOnlyDevice : public BusDevice { 68 | public: 69 | using BusDevice::BusDevice; 70 | 71 | inline uint8_t read(uint16_t) { 72 | return 0; 73 | } 74 | }; 75 | } // namespace M6502 76 | -------------------------------------------------------------------------------- /include/bus.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the MOS 6502 Emulator 3 | * (https://github.com/KCreate/mos6502) 4 | * 5 | * MIT License 6 | * 7 | * Copyright (c) 2017 - 2018 Leonard Schütz 8 | * 9 | * Permission is hereby granted, free of charge, to any person obtaining a copy 10 | * of this software and associated documentation files (the "Software"), to deal 11 | * in the Software without restriction, including without limitation the rights 12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | * copies of the Software, and to permit persons to whom the Software is 14 | * furnished to do so, subject to the following conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be included in 17 | * all copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 25 | * SOFTWARE. 26 | */ 27 | 28 | #include 29 | #include 30 | 31 | #include "busdevice.h" 32 | 33 | #pragma once 34 | 35 | namespace M6502 { 36 | 37 | // Addresses on the bus of the attached devices 38 | static constexpr uint16_t kAddrRAM = 0x0000; 39 | static constexpr uint16_t kAddrIO = 0x4000; 40 | static constexpr uint16_t kAddrROM = 0x4920; 41 | 42 | // Sizes of different attached devices 43 | static constexpr size_t kSizeRAM = 0x4000; 44 | static constexpr size_t kSizeIO = 0x920; 45 | static constexpr size_t kSizeROM = 0xB6E0; 46 | 47 | // Forward declaration 48 | class CPU; 49 | 50 | // Abstraction of a bus attached to the 6502 micrcontroller 51 | class Bus { 52 | public: 53 | Bus() { 54 | } 55 | 56 | // Read access 57 | uint8_t read_byte(uint16_t address); 58 | uint16_t read_word(uint16_t address); 59 | 60 | // Write access 61 | void write_byte(uint16_t address, uint8_t value); 62 | void write_word(uint16_t address, uint16_t value); 63 | 64 | // Return the device which handles a given address 65 | BusDevice* resolve_address_to_device(uint16_t address); 66 | 67 | // Attach devices to the different parts of the bus 68 | void attach_cpu(CPU* cpu); 69 | void attach_ram(BusDevice* dev); 70 | void attach_io(BusDevice* dev); 71 | void attach_rom(BusDevice* dev); 72 | 73 | // Interrupts 74 | void int_irq(); 75 | void int_nmi(); 76 | void int_res(); 77 | 78 | private: 79 | // Attached devices 80 | CPU* cpu; 81 | BusDevice* RAM = nullptr; 82 | BusDevice* IO = nullptr; 83 | BusDevice* ROM = nullptr; 84 | }; 85 | } // namespace M6502 86 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | # BasedOnStyle: Chromium 4 | AccessModifierOffset: -2 5 | AlignAfterOpenBracket: Align 6 | AlignConsecutiveAssignments: false 7 | AlignConsecutiveDeclarations: false 8 | AlignEscapedNewlines: Left 9 | AlignOperands: true 10 | AlignTrailingComments: true 11 | AllowAllParametersOfDeclarationOnNextLine: false 12 | AllowShortBlocksOnASingleLine: false 13 | AllowShortCaseLabelsOnASingleLine: true 14 | AllowShortFunctionsOnASingleLine: false 15 | AllowShortIfStatementsOnASingleLine: false 16 | AllowShortLoopsOnASingleLine: false 17 | AlwaysBreakAfterDefinitionReturnType: None 18 | AlwaysBreakAfterReturnType: None 19 | AlwaysBreakBeforeMultilineStrings: true 20 | AlwaysBreakTemplateDeclarations: true 21 | BinPackArguments: true 22 | BinPackParameters: false 23 | BraceWrapping: 24 | AfterClass: false 25 | AfterControlStatement: false 26 | AfterEnum: false 27 | AfterFunction: false 28 | AfterNamespace: false 29 | AfterObjCDeclaration: false 30 | AfterStruct: false 31 | AfterUnion: false 32 | BeforeCatch: false 33 | BeforeElse: false 34 | IndentBraces: false 35 | SplitEmptyFunction: true 36 | SplitEmptyRecord: true 37 | SplitEmptyNamespace: true 38 | BreakBeforeBinaryOperators: None 39 | BreakBeforeBraces: Attach 40 | BreakBeforeInheritanceComma: false 41 | BreakBeforeTernaryOperators: true 42 | BreakConstructorInitializersBeforeComma: false 43 | BreakConstructorInitializers: BeforeColon 44 | BreakStringLiterals: true 45 | ColumnLimit: 120 46 | CommentPragmas: '^ IWYU pragma:' 47 | CompactNamespaces: false 48 | ConstructorInitializerAllOnOneLineOrOnePerLine: true 49 | ConstructorInitializerIndentWidth: 4 50 | ContinuationIndentWidth: 4 51 | Cpp11BracedListStyle: true 52 | DerivePointerAlignment: false 53 | DisableFormat: false 54 | ExperimentalAutoDetectBinPacking: false 55 | ForEachMacros: 56 | - foreach 57 | - Q_FOREACH 58 | - BOOST_FOREACH 59 | IncludeCategories: 60 | - Regex: '^<.*\.h>' 61 | Priority: 1 62 | - Regex: '^<.*' 63 | Priority: 2 64 | - Regex: '.*' 65 | Priority: 3 66 | IncludeIsMainRegex: '([-_](test|unittest))?$' 67 | IndentCaseLabels: true 68 | IndentWidth: 2 69 | IndentWrappedFunctionNames: false 70 | KeepEmptyLinesAtTheStartOfBlocks: false 71 | MacroBlockBegin: '' 72 | MacroBlockEnd: '' 73 | MaxEmptyLinesToKeep: 1 74 | NamespaceIndentation: None 75 | PenaltyBreakAssignment: 2 76 | PenaltyBreakBeforeFirstCallParameter: 1 77 | PenaltyBreakComment: 300 78 | PenaltyBreakFirstLessLess: 120 79 | PenaltyBreakString: 1000 80 | PenaltyExcessCharacter: 1000000 81 | PenaltyReturnTypeOnItsOwnLine: 200 82 | PointerAlignment: Left 83 | ReflowComments: true 84 | SortIncludes: true 85 | SortUsingDeclarations: true 86 | SpaceAfterCStyleCast: false 87 | SpaceAfterTemplateKeyword: true 88 | SpaceBeforeAssignmentOperators: true 89 | SpaceBeforeParens: ControlStatements 90 | SpaceInEmptyParentheses: false 91 | SpacesBeforeTrailingComments: 2 92 | SpacesInAngles: false 93 | SpacesInContainerLiterals: true 94 | SpacesInCStyleCastParentheses: false 95 | SpacesInParentheses: false 96 | SpacesInSquareBrackets: false 97 | Standard: Auto 98 | TabWidth: 2 99 | UseTab: Never 100 | ... 101 | 102 | -------------------------------------------------------------------------------- /src/bus.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the MOS 6502 Emulator 3 | * (https://github.com/KCreate/mos6502) 4 | * 5 | * MIT License 6 | * 7 | * Copyright (c) 2017 - 2018 Leonard Schütz 8 | * 9 | * Permission is hereby granted, free of charge, to any person obtaining a copy 10 | * of this software and associated documentation files (the "Software"), to deal 11 | * in the Software without restriction, including without limitation the rights 12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | * copies of the Software, and to permit persons to whom the Software is 14 | * furnished to do so, subject to the following conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be included in 17 | * all copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 25 | * SOFTWARE. 26 | */ 27 | 28 | #include "bus.h" 29 | #include "cpu.h" 30 | 31 | namespace M6502 { 32 | 33 | uint8_t Bus::read_byte(uint16_t address) { 34 | BusDevice* dev = this->resolve_address_to_device(address); 35 | if (dev == nullptr) 36 | return 0; 37 | return dev->read(address - dev->mapped_address); 38 | } 39 | 40 | uint16_t Bus::read_word(uint16_t address) { 41 | BusDevice* dev = this->resolve_address_to_device(address); 42 | if (dev == nullptr) 43 | return 0; 44 | uint16_t result; 45 | result = dev->read(address - dev->mapped_address); 46 | result |= (dev->read(address - dev->mapped_address + 1) << 8); 47 | return result; 48 | } 49 | 50 | void Bus::write_byte(uint16_t address, uint8_t value) { 51 | BusDevice* dev = this->resolve_address_to_device(address); 52 | if (dev == nullptr) 53 | return; 54 | dev->write(address - dev->mapped_address, value); 55 | } 56 | 57 | void Bus::write_word(uint16_t address, uint16_t value) { 58 | BusDevice* dev = this->resolve_address_to_device(address); 59 | if (dev == nullptr) 60 | return; 61 | dev->write(address - dev->mapped_address, value & 0xFF); 62 | dev->write(address - dev->mapped_address + 1, (value >> 8) & 0xFF); 63 | } 64 | 65 | void Bus::attach_cpu(CPU* cpu) { 66 | this->cpu = cpu; 67 | cpu->bus = this; 68 | } 69 | 70 | void Bus::attach_ram(BusDevice* dev) { 71 | this->RAM = dev; 72 | dev->bus = this; 73 | } 74 | 75 | void Bus::attach_io(BusDevice* dev) { 76 | this->IO = dev; 77 | dev->bus = this; 78 | } 79 | 80 | void Bus::attach_rom(BusDevice* dev) { 81 | this->ROM = dev; 82 | dev->bus = this; 83 | } 84 | 85 | void Bus::int_irq() { 86 | this->cpu->int_irq = true; 87 | this->cpu->cv_int.notify_one(); 88 | } 89 | 90 | void Bus::int_nmi() { 91 | this->cpu->int_nmi = true; 92 | this->cpu->cv_int.notify_one(); 93 | } 94 | 95 | void Bus::int_res() { 96 | this->cpu->int_res = true; 97 | this->cpu->cv_int.notify_one(); 98 | } 99 | 100 | BusDevice* Bus::resolve_address_to_device(uint16_t address) { 101 | if (address < kAddrIO) 102 | return this->RAM; 103 | if (address >= kAddrROM) 104 | return this->ROM; 105 | return this->IO; 106 | } 107 | 108 | } // namespace M6502 109 | -------------------------------------------------------------------------------- /examples/c/pong_oc.asm: -------------------------------------------------------------------------------- 1 | 0x78, 2 | 0x20, 0x79, 0x49, 3 | 0xA9, 0x10, 4 | 0x8D, 0x06, 0x49, 5 | 0x58, 6 | 0xEA, 7 | 0x4C, 0x2A, 0x49, 8 | 0xAD, 0x03, 0x49, 9 | 0xC9, 0x01, 10 | 0xF0, 0x0B, 11 | 0xC9, 0x06, 12 | 0xF0, 0x0D, 13 | 0xC9, 0x08, 14 | 0xF0, 0x0F, 15 | 0x4C, 0x52, 0x49, 16 | 0x20, 0x83, 0x49, 17 | 0x4C, 0x52, 0x49, 18 | 0x20, 0xD4, 0x49, 19 | 0x4C, 0x52, 0x49, 20 | 0x20, 0x73, 0x49, 21 | 0x4C, 0x52, 0x49, 22 | 0x40, 23 | 0xA9, 0xD9, 24 | 0x8D, 0x08, 0x49, 25 | 0xA9, 0x00, 26 | 0x8D, 0x11, 0x49, 27 | 0xA9, 0x0A, 28 | 0x8D, 0x10, 0x49, 29 | 0x60, 30 | 0xA9, 0xD9, 31 | 0x8D, 0x08, 0x49, 32 | 0xA9, 0x00, 33 | 0x8D, 0x11, 0x49, 34 | 0xA9, 0x1E, 35 | 0x8D, 0x10, 0x49, 36 | 0x60, 37 | 0xA9, 0x19, 38 | 0x8D, 0x08, 0x49, 39 | 0x60, 40 | 0xA9, 0x00, 41 | 0x85, 0x02, 42 | 0x85, 0x03, 43 | 0x20, 0xF0, 0x49, 44 | 0x60, 45 | 0xAD, 0x04, 0x49, 46 | 0x85, 0x08, 47 | 0xC9, 0x16, 48 | 0xF0, 0x0F, 49 | 0xC9, 0x12, 50 | 0xF0, 0x19, 51 | 0xC9, 0x08, 52 | 0xF0, 0x23, 53 | 0xC9, 0x0A, 54 | 0xF0, 0x2D, 55 | 0x4C, 0xD3, 0x49, 56 | 0xA5, 0x00, 57 | 0xC9, 0x00, 58 | 0xF0, 0x32, 59 | 0x18, 60 | 0xE9, 0x01, 61 | 0x85, 0x00, 62 | 0x4C, 0xD3, 0x49, 63 | 0xA5, 0x00, 64 | 0xC9, 0x1B, 65 | 0xF0, 0x24, 66 | 0x18, 67 | 0x69, 0x01, 68 | 0x85, 0x00, 69 | 0x4C, 0xD3, 0x49, 70 | 0xA5, 0x01, 71 | 0xC9, 0x00, 72 | 0xF0, 0x16, 73 | 0x18, 74 | 0xE9, 0x01, 75 | 0x85, 0x01, 76 | 0x4C, 0xD3, 0x49, 77 | 0xA5, 0x01, 78 | 0xC9, 0x1B, 79 | 0xF0, 0x08, 80 | 0x18, 81 | 0x69, 0x01, 82 | 0x85, 0x01, 83 | 0x4C, 0xD3, 0x49, 84 | 0x60, 85 | 0x20, 0xA4, 0x4A, 86 | 0x20, 0xFF, 0x49, 87 | 0x20, 0x31, 0x4A, 88 | 0x20, 0xB4, 0x4A, 89 | 0x20, 0xD4, 0x4A, 90 | 0x20, 0xC4, 0x4A, 91 | 0x20, 0xEC, 0x4A, 92 | 0x20, 0x1D, 0x4B, 93 | 0x20, 0x2D, 0x4B, 94 | 0x60, 95 | 0xA9, 0x20, 96 | 0x85, 0x04, 97 | 0x85, 0x05, 98 | 0xA9, 0x01, 99 | 0x85, 0x06, 100 | 0xA9, 0x01, 101 | 0x85, 0x07, 102 | 0x60, 103 | 0xA5, 0x06, 104 | 0xD0, 0x0A, 105 | 0xA5, 0x04, 106 | 0x18, 107 | 0xE9, 0x01, 108 | 0x85, 0x04, 109 | 0x4C, 0x17, 0x4A, 110 | 0xA5, 0x04, 111 | 0x18, 112 | 0x69, 0x01, 113 | 0x85, 0x04, 114 | 0x4C, 0x17, 0x4A, 115 | 0xA5, 0x07, 116 | 0xD0, 0x0A, 117 | 0xA5, 0x05, 118 | 0x18, 119 | 0xE9, 0x01, 120 | 0x85, 0x05, 121 | 0x4C, 0x30, 0x4A, 122 | 0xA5, 0x05, 123 | 0x18, 124 | 0x69, 0x01, 125 | 0x85, 0x05, 126 | 0xEA, 127 | 0x4C, 0x30, 0x4A, 128 | 0x60, 129 | 0xA5, 0x05, 130 | 0xC9, 0x00, 131 | 0xD0, 0x07, 132 | 0xA9, 0x01, 133 | 0x85, 0x07, 134 | 0x20, 0x53, 0x49, 135 | 0xA5, 0x05, 136 | 0xC9, 0x23, 137 | 0xD0, 0x07, 138 | 0xA9, 0x00, 139 | 0x85, 0x07, 140 | 0x20, 0x53, 0x49, 141 | 0xA5, 0x05, 142 | 0xC5, 0x00, 143 | 0xB0, 0x16, 144 | 0xA5, 0x05, 145 | 0x18, 146 | 0xE5, 0x00, 147 | 0xC9, 0x08, 148 | 0x90, 0x0D, 149 | 0xA5, 0x04, 150 | 0xC9, 0x02, 151 | 0xD0, 0x07, 152 | 0xA9, 0x01, 153 | 0x85, 0x06, 154 | 0x20, 0x53, 0x49, 155 | 0xA5, 0x05, 156 | 0xC5, 0x01, 157 | 0xB0, 0x16, 158 | 0xA5, 0x05, 159 | 0x18, 160 | 0xE5, 0x01, 161 | 0xC9, 0x08, 162 | 0x90, 0x0D, 163 | 0xA5, 0x04, 164 | 0xC9, 0x02, 165 | 0xD0, 0x07, 166 | 0xA9, 0x00, 167 | 0x85, 0x06, 168 | 0x20, 0x53, 0x49, 169 | 0xA5, 0x04, 170 | 0xC9, 0x00, 171 | 0xD0, 0x0D, 172 | 0xA5, 0x03, 173 | 0x18, 174 | 0x69, 0x01, 175 | 0x85, 0x03, 176 | 0x20, 0x5F, 0x4B, 177 | 0x20, 0x63, 0x49, 178 | 0xA5, 0x02, 179 | 0x18, 180 | 0x69, 0x01, 181 | 0x85, 0x02, 182 | 0x20, 0x5F, 0x4B, 183 | 0x20, 0x63, 0x49, 184 | 0x60, 185 | 0xA5, 0x02, 186 | 0xC9, 0x0A, 187 | 0xF0, 0x06, 188 | 0xA5, 0x03, 189 | 0xC9, 0x0A, 190 | 0xF0, 0x00, 191 | 0x20, 0x79, 0x49, 192 | 0x60, 193 | 0xA9, 0x00, 194 | 0x8D, 0x0C, 0x49, 195 | 0xA9, 0x80, 196 | 0x8D, 0x0B, 0x49, 197 | 0xA9, 0x81, 198 | 0x8D, 0x0B, 0x49, 199 | 0x60, 200 | 0xA9, 0xFF, 201 | 0x8D, 0x0C, 0x49, 202 | 0xA9, 0x80, 203 | 0x8D, 0x0B, 0x49, 204 | 0xA9, 0x81, 205 | 0x8D, 0x0B, 0x49, 206 | 0x60, 207 | 0xA9, 0x00, 208 | 0x8D, 0x0C, 0x49, 209 | 0x8D, 0x0D, 0x49, 210 | 0xA9, 0x40, 211 | 0x8D, 0x0E, 0x49, 212 | 0xA9, 0x24, 213 | 0x8D, 0x0F, 0x49, 214 | 0xA9, 0x00, 215 | 0x8D, 0x0B, 0x49, 216 | 0x60, 217 | 0xA9, 0x01, 218 | 0x8D, 0x0C, 0x49, 219 | 0x8D, 0x0E, 0x49, 220 | 0xA5, 0x00, 221 | 0x8D, 0x0D, 0x49, 222 | 0x18, 223 | 0x69, 0x08, 224 | 0x8D, 0x0F, 0x49, 225 | 0xA9, 0x03, 226 | 0x8D, 0x0B, 0x49, 227 | 0xA9, 0x3E, 228 | 0x8D, 0x0C, 0x49, 229 | 0x8D, 0x0E, 0x49, 230 | 0xA5, 0x01, 231 | 0x8D, 0x0D, 0x49, 232 | 0x18, 233 | 0x69, 0x08, 234 | 0x8D, 0x0F, 0x49, 235 | 0xA9, 0x03, 236 | 0x8D, 0x0B, 0x49, 237 | 0x60, 238 | 0xA5, 0x04, 239 | 0x8D, 0x0C, 0x49, 240 | 0xA5, 0x05, 241 | 0x8D, 0x0D, 0x49, 242 | 0xA9, 0x02, 243 | 0x8D, 0x0B, 0x49, 244 | 0x60, 245 | 0xA9, 0x00, 246 | 0x8D, 0x0C, 0x49, 247 | 0xA9, 0x23, 248 | 0x8D, 0x0D, 0x49, 249 | 0xA5, 0x02, 250 | 0x8D, 0x0E, 0x49, 251 | 0xA9, 0x23, 252 | 0x8D, 0x0F, 0x49, 253 | 0xA9, 0x03, 254 | 0x8D, 0x0B, 0x49, 255 | 0xA9, 0x00, 256 | 0x8D, 0x0C, 0x49, 257 | 0xA9, 0x22, 258 | 0x8D, 0x0D, 0x49, 259 | 0xA5, 0x03, 260 | 0x8D, 0x0E, 0x49, 261 | 0xA9, 0x22, 262 | 0x8D, 0x0F, 0x49, 263 | 0xA9, 0x03, 264 | 0x8D, 0x0B, 0x49, 265 | 0x60, 266 | 0xA9, 0x20, 267 | 0x85, 0x04, 268 | 0x85, 0x05, 269 | 0xA9, 0x01, 270 | 0x85, 0x06, 271 | 0xA9, 0x01, 272 | 0x85, 0x07, 273 | 0x60 274 | -------------------------------------------------------------------------------- /examples/c/pong_oc_annotated_2.asm: -------------------------------------------------------------------------------- 1 | // RST 2 | 0x78 3 | 0x20 0x79 0x49 4 | 0xa9 0x10 5 | 0x8d 0x06 0x49 6 | 0x58 7 | 8 | // L_MAIN_LOOP 9 | 0xea 10 | 0x4c 0x2a 0x49 11 | 12 | // L_IRQ 13 | ad 03 49 14 | c9 01 15 | f0 0b 16 | c9 06 17 | f0 0d 18 | c9 08 19 | f0 0f 20 | 4c 52 49 21 | 22 | // L_IRQ_KEYDOWM 23 | 20 83 49 24 | 4c 52 49 25 | 26 | // L_IRQ_CLOCK 27 | 20 d4 49 28 | 4c 52 49 29 | 30 | // L_IRQ_TIMER 31 | 20 73 49 32 | 4c 52 49 33 | 34 | // L_IRQ_EXIT 35 | 40 36 | 37 | // L_START_SHORT_BEEP 38 | a9 d9 39 | 8d 08 49 40 | a9 00 41 | 8d 11 49 42 | a9 0a 43 | 8d 10 49 44 | 60 45 | 46 | // L_START_LONG_BEEP 47 | a9 d9 48 | 8d 08 49 49 | a9 00 50 | 8d 11 49 51 | a9 1e 52 | 8d 10 49 53 | 60 54 | 55 | // L_STOP_BEEP 56 | a9 19 57 | 8d 08 49 58 | 60 59 | 60 | // L_RESET_GAME 61 | a9 00 62 | 85 02 63 | 85 03 64 | 20 f0 49 65 | 60 66 | 67 | // L_HANDLE_KEYPRESS 68 | ad 04 49 69 | 85 08 70 | c9 16 71 | f0 0f 72 | c9 12 73 | f0 19 74 | c9 08 75 | f0 23 76 | c9 0a 77 | f0 2d 78 | 4c d3 49 79 | 80 | // L_HANDLE_KEYPRESS_W 81 | a5 00 82 | c9 00 83 | f0 32 84 | 18 85 | e9 01 86 | 85 00 87 | 4c d3 49 88 | 89 | // L_HANDLE_KEYPRESS_S 90 | a5 00 91 | c9 1b 92 | f0 24 93 | 18 94 | 69 01 95 | 85 00 96 | 4c d3 49 97 | 98 | // L_HANDLE_KEYPRESS_I 99 | a5 01 100 | c9 00 101 | f0 16 102 | 18 103 | e9 01 104 | 85 01 105 | 4c d3 49 106 | 107 | // L_HANDLE_KEYPRESS_K 108 | a5 01 109 | c9 1b 110 | f0 08 111 | 18 112 | 69 01 113 | 85 01 114 | 4c d3 49 115 | 116 | // L_HANDLE_KEYPRESS_EXIT 117 | 60 118 | 119 | // L_HANDLE_CLOCK 120 | 20 a4 4a 121 | 20 ff 49 122 | 20 31 4a 123 | 20 b4 4a 124 | 20 d4 4a 125 | 20 c4 4a 126 | 20 ec 4a 127 | 20 1d 4b 128 | 20 2d 4b 129 | 60 130 | 131 | // L_RESET_POSITIONS 132 | a9 20 133 | 85 04 134 | 85 05 135 | a9 01 136 | 85 06 137 | a9 01 138 | 85 07 139 | 60 140 | 141 | // L_UPDATE_BALL 142 | a5 06 143 | d0 0a 144 | a5 04 145 | 18 146 | e9 01 147 | 85 04 148 | 4c 17 4a 149 | 150 | // L_UPDATE_BALL_X_INC 151 | a5 04 152 | 18 153 | 69 01 154 | 85 04 155 | 4c 17 4a 156 | 157 | // L_UPDATE_BALL_X_END 158 | a5 07 159 | d0 0a 160 | a5 05 161 | 18 162 | e9 01 163 | 85 05 164 | 4c 30 4a 165 | 166 | // L_UPDATE_BALL_Y_INC 167 | a5 05 168 | 18 169 | 69 01 170 | 85 05 171 | ea 172 | 4c 30 4a 173 | 174 | // L_UPDATE_BALL_Y_END 175 | 60 176 | 177 | // L_CHECK_BALL_COLLISIONS 178 | a5 05 179 | c9 00 180 | d0 07 181 | a9 01 182 | 85 07 183 | 20 53 49 184 | 185 | // L_CHECK_BALL_COLLISIONS_NO_TOP_HIT 186 | a5 05 187 | c9 23 188 | d0 07 189 | a9 00 190 | 85 07 191 | 20 53 49 192 | 193 | // L_CHECK_BALL_COLLISIONS_NO_BOTTOM_HIT 194 | a5 05 195 | c5 00 196 | b0 16 197 | a5 05 198 | 18 199 | e5 00 200 | c9 08 201 | 90 0d 202 | a5 04 203 | c9 02 204 | d0 07 205 | a9 01 206 | 85 06 207 | 20 53 49 208 | 209 | // L_CHECK_BALL_COLLISIONS_NO_LEFT_HIT 210 | a5 05 211 | c5 01 212 | b0 16 213 | a5 05 214 | 18 215 | e5 01 216 | c9 08 217 | 90 0d 218 | a5 04 219 | c9 02 220 | d0 07 221 | a9 00 222 | 85 06 223 | 20 53 49 224 | 225 | // L_CHECK_BALL_COLLISIONS_NO_RIGHT_HIT 226 | a5 04 227 | c9 00 228 | d0 0d 229 | a5 03 230 | 18 231 | 69 01 232 | 85 03 233 | 20 5f 4b 234 | 20 63 49 235 | 236 | // L_CHECK_BALL_COLLISIONS_INC_1_SCORE 237 | a5 02 238 | 18 239 | 69 01 240 | 85 02 241 | 20 5f 4b 242 | 20 63 49 243 | 244 | // L_CHECK_BALL_COLLISIONS_EXIT 245 | 60 246 | 247 | // L_CHECK_SCORE 248 | a5 02 249 | c9 0a 250 | f0 06 251 | a5 03 252 | c9 0a 253 | f0 00 254 | 255 | // L_CHECK_SCORE_RESET 256 | 20 79 49 257 | 258 | // L_CHECK_SCORE_END 259 | 60 260 | 261 | // L_SET_COLOR_BLACK 262 | a9 00 263 | 8d 0c 49 264 | a9 80 265 | 8d 0b 49 266 | a9 81 267 | 8d 0b 49 268 | 60 269 | 270 | // L_SET_COLOR_WHITE 271 | a9 ff 272 | 8d 0c 49 273 | a9 80 274 | 8d 0b 49 275 | a9 81 276 | 8d 0b 49 277 | 60 278 | 279 | // L_CLEAR_SCREEN 280 | a9 00 281 | 8d 0c 49 282 | 8d 0d 49 283 | a9 40 284 | 8d 0e 49 285 | a9 24 286 | 8d 0f 49 287 | a9 00 288 | 8d 0b 49 289 | 60 290 | 291 | // L_DRAW_RACKETS 292 | a9 01 293 | 8d 0c 49 294 | 8d 0e 49 295 | a5 00 296 | 8d 0d 49 297 | 18 298 | 69 08 299 | 8d 0f 49 300 | a9 03 301 | 8d 0b 49 302 | a9 3e 303 | 8d 0c 49 304 | 8d 0e 49 305 | a5 01 306 | 8d 0d 49 307 | 18 308 | 69 08 309 | 8d 0f 49 310 | a9 03 311 | 8d 0b 49 312 | 60 313 | 314 | // L_DRAW_BALL 315 | a5 04 316 | 8d 0c 49 317 | a5 05 318 | 8d 0d 49 319 | a9 02 320 | 8d 0b 49 321 | 60 322 | 323 | // L_DRAW_SCORE 324 | a9 00 325 | 8d 0c 49 326 | a9 23 327 | 8d 0d 49 328 | a5 02 329 | 8d 0e 49 330 | a9 23 331 | 8d 0f 49 332 | a9 03 333 | 8d 0b 49 334 | a9 00 335 | 8d 0c 49 336 | a9 22 337 | 8d 0d 49 338 | a5 03 339 | 8d 0e 49 340 | a9 22 341 | 8d 0f 49 342 | a9 03 343 | 8d 0b 49 344 | 60 345 | 346 | // L_RESET_BALL_POSITION 347 | a9 20 348 | 85 04 349 | 85 05 350 | a9 01 351 | 85 06 352 | a9 01 353 | 85 07 354 | 60 355 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the MOS 6502 Emulator 3 | * (https://github.com/KCreate/mos6502) 4 | * 5 | * MIT License 6 | * 7 | * Copyright (c) 2017 - 2018 Leonard Schütz 8 | * 9 | * Permission is hereby granted, free of charge, to any person obtaining a copy 10 | * of this software and associated documentation files (the "Software"), to deal 11 | * in the Software without restriction, including without limitation the rights 12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | * copies of the Software, and to permit persons to whom the Software is 14 | * furnished to do so, subject to the following conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be included in 17 | * all copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 25 | * SOFTWARE. 26 | */ 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #include "bus.h" 34 | #include "cpu.h" 35 | #include "iochip.h" 36 | #include "rammodule.h" 37 | #include "rommodule.h" 38 | 39 | using namespace M6502; 40 | 41 | int main() { 42 | using namespace std::chrono_literals; 43 | 44 | // Create the machine parts 45 | RAMModule ram(kAddrRAM); 46 | IOChip io(kAddrIO); 47 | ROMModule rom(kAddrROM); 48 | 49 | // Create the bus and attach all the devices 50 | Bus bus; 51 | bus.attach_ram(&ram); 52 | bus.attach_io(&io); 53 | bus.attach_rom(&rom); 54 | 55 | // Set the reset vector 56 | rom.get_buffer()[kVecRES - kAddrROM] = 0x20; 57 | rom.get_buffer()[kVecRES - kAddrROM + 1] = 0x49; 58 | 59 | // Flash some code into the ROM for the CPU to execute 60 | uint8_t code[] = { 61 | 0x78, 0x20, 0x79, 0x49, 0xa9, 0x10, 0x8d, 0x06, 0x49, 0x58, 0xea, 0x4c, 0x2a, 0x49, 0xad, 0x03, 62 | 0x49, 0xc9, 0x01, 0xf0, 0x0b, 0xc9, 0x06, 0xf0, 0x0d, 0xc9, 0x08, 0xf0, 0x0f, 0x4c, 0x52, 0x49, 63 | 0x20, 0x85, 0x49, 0x4c, 0x52, 0x49, 0x20, 0xd6, 0x49, 0x4c, 0x52, 0x49, 0x20, 0x73, 0x49, 0x4c, 64 | 0x52, 0x49, 0x40, 0xa9, 0xd9, 0x8d, 0x08, 0x49, 0xa9, 0x00, 0x8d, 0x11, 0x49, 0xa9, 0x0a, 0x8d, 65 | 0x10, 0x49, 0x60, 0xa9, 0xd9, 0x8d, 0x08, 0x49, 0xa9, 0x00, 0x8d, 0x11, 0x49, 0xa9, 0x1e, 0x8d, 66 | 0x10, 0x49, 0x60, 0xa9, 0x19, 0x8d, 0x08, 0x49, 0x60, 0xa9, 0x00, 0x85, 0x02, 0xa9, 0x00, 0x85, 67 | 0x03, 0x20, 0xf2, 0x49, 0x60, 0xad, 0x04, 0x49, 0x85, 0x08, 0xc9, 0x16, 0xf0, 0x0f, 0xc9, 0x12, 68 | 0xf0, 0x19, 0xc9, 0x08, 0xf0, 0x23, 0xc9, 0x0a, 0xf0, 0x2d, 0x4c, 0xd5, 0x49, 0xa5, 0x00, 0xc9, 69 | 0x00, 0xf0, 0x32, 0x18, 0xe9, 0x01, 0x85, 0x00, 0x4c, 0xd5, 0x49, 0xa5, 0x00, 0xc9, 0x1c, 0xf0, 70 | 0x24, 0x18, 0x69, 0x01, 0x85, 0x00, 0x4c, 0xd5, 0x49, 0xa5, 0x01, 0xc9, 0x00, 0xf0, 0x16, 0x18, 71 | 0xe9, 0x01, 0x85, 0x01, 0x4c, 0xd5, 0x49, 0xa5, 0x01, 0xc9, 0x1c, 0xf0, 0x08, 0x18, 0x69, 0x01, 72 | 0x85, 0x01, 0x4c, 0xd5, 0x49, 0x60, 0x20, 0xba, 0x4a, 0x20, 0x0f, 0x4a, 0x20, 0x41, 0x4a, 0x20, 73 | 0xcd, 0x4a, 0x20, 0x0d, 0x4b, 0x20, 0xdd, 0x4a, 0x20, 0x25, 0x4b, 0x20, 0x56, 0x4b, 0x20, 0x66, 74 | 0x4b, 0x60, 0xa9, 0x0e, 0x85, 0x00, 0xa9, 0x0e, 0x85, 0x01, 0x20, 0xfe, 0x49, 0x60, 0xa9, 0x20, 75 | 0x85, 0x04, 0xa9, 0x12, 0x85, 0x05, 0xa9, 0x01, 0x85, 0x06, 0xa9, 0x01, 0x85, 0x07, 0x60, 0xa5, 76 | 0x06, 0xd0, 0x0a, 0xa5, 0x04, 0x18, 0xe9, 0x01, 0x85, 0x04, 0x4c, 0x27, 0x4a, 0xa5, 0x04, 0x18, 77 | 0x69, 0x01, 0x85, 0x04, 0x4c, 0x27, 0x4a, 0xa5, 0x07, 0xd0, 0x0a, 0xa5, 0x05, 0x18, 0xe9, 0x01, 78 | 0x85, 0x05, 0x4c, 0x40, 0x4a, 0xa5, 0x05, 0x18, 0x69, 0x01, 0x85, 0x05, 0xea, 0x4c, 0x40, 0x4a, 79 | 0x60, 0xa5, 0x05, 0xc9, 0x00, 0xd0, 0x07, 0xa9, 0x01, 0x85, 0x07, 0x20, 0x53, 0x49, 0xa5, 0x05, 80 | 0xc9, 0x23, 0xd0, 0x07, 0xa9, 0x00, 0x85, 0x07, 0x20, 0x53, 0x49, 0xa5, 0x05, 0xc5, 0x00, 0x90, 81 | 0x16, 0xa5, 0x05, 0x18, 0xe5, 0x00, 0xc9, 0x08, 0xb0, 0x0d, 0xa5, 0x04, 0xc9, 0x02, 0xd0, 0x07, 82 | 0xa9, 0x01, 0x85, 0x06, 0x20, 0x53, 0x49, 0xa5, 0x05, 0xc5, 0x01, 0x90, 0x16, 0xa5, 0x05, 0x18, 83 | 0xe5, 0x01, 0xc9, 0x08, 0xb0, 0x0d, 0xa5, 0x04, 0xc9, 0x3d, 0xd0, 0x07, 0xa9, 0x00, 0x85, 0x06, 84 | 0x20, 0x53, 0x49, 0xa5, 0x04, 0xc9, 0x00, 0xd0, 0x0d, 0xa5, 0x03, 0x18, 0x69, 0x01, 0x85, 0x03, 85 | 0x20, 0xfe, 0x49, 0x20, 0x63, 0x49, 0xa5, 0x04, 0xc9, 0x3f, 0xd0, 0x0d, 0xa5, 0x02, 0x18, 0x69, 86 | 0x01, 0x85, 0x02, 0x20, 0xfe, 0x49, 0x20, 0x63, 0x49, 0x60, 0xa5, 0x02, 0xc9, 0x0a, 0xf0, 0x09, 87 | 0xa5, 0x03, 0xc9, 0x0a, 0xf0, 0x03, 0x4c, 0xcc, 0x4a, 0x20, 0x79, 0x49, 0x60, 0xa9, 0x00, 0x8d, 88 | 0x0c, 0x49, 0xa9, 0x80, 0x8d, 0x0b, 0x49, 0xa9, 0x81, 0x8d, 0x0b, 0x49, 0x60, 0xa9, 0xff, 0x8d, 89 | 0x0c, 0x49, 0xa9, 0x80, 0x8d, 0x0b, 0x49, 0xa9, 0x81, 0x8d, 0x0b, 0x49, 0x60, 0xa9, 0x03, 0x8d, 90 | 0x0c, 0x49, 0xa9, 0x80, 0x8d, 0x0b, 0x49, 0xa9, 0x81, 0x8d, 0x0b, 0x49, 0x60, 0xa9, 0xfc, 0x8d, 91 | 0x0c, 0x49, 0xa9, 0x80, 0x8d, 0x0b, 0x49, 0xa9, 0x81, 0x8d, 0x0b, 0x49, 0x60, 0xa9, 0x00, 0x8d, 92 | 0x0c, 0x49, 0x8d, 0x0d, 0x49, 0xa9, 0x40, 0x8d, 0x0e, 0x49, 0xa9, 0x24, 0x8d, 0x0f, 0x49, 0xa9, 93 | 0x00, 0x8d, 0x0b, 0x49, 0x60, 0xa9, 0x01, 0x8d, 0x0c, 0x49, 0x8d, 0x0e, 0x49, 0xa5, 0x00, 0x8d, 94 | 0x0d, 0x49, 0x18, 0x69, 0x08, 0x8d, 0x0f, 0x49, 0xa9, 0x03, 0x8d, 0x0b, 0x49, 0xa9, 0x3e, 0x8d, 95 | 0x0c, 0x49, 0x8d, 0x0e, 0x49, 0xa5, 0x01, 0x8d, 0x0d, 0x49, 0x18, 0x69, 0x08, 0x8d, 0x0f, 0x49, 96 | 0xa9, 0x03, 0x8d, 0x0b, 0x49, 0x60, 0xa5, 0x04, 0x8d, 0x0c, 0x49, 0xa5, 0x05, 0x8d, 0x0d, 0x49, 97 | 0xa9, 0x02, 0x8d, 0x0b, 0x49, 0x60, 0x20, 0xfd, 0x4a, 0xa9, 0x00, 0x8d, 0x0c, 0x49, 0xa9, 0x23, 98 | 0x8d, 0x0d, 0x49, 0xa5, 0x02, 0x8d, 0x0e, 0x49, 0xa9, 0x23, 0x8d, 0x0f, 0x49, 0xa9, 0x03, 0x8d, 99 | 0x0b, 0x49, 0x20, 0xed, 0x4a, 0xa9, 0x00, 0x8d, 0x0c, 0x49, 0xa9, 0x22, 0x8d, 0x0d, 0x49, 0xa5, 100 | 0x03, 0x8d, 0x0e, 0x49, 0xa9, 0x22, 0x8d, 0x0f, 0x49, 0xa9, 0x03, 0x8d, 0x0b, 0x49, 0x20, 0xdd, 101 | 0x4a, 0xa9, 0x0a, 0x8d, 0x0c, 0x49, 0xa9, 0x22, 0x8d, 0x0d, 0x49, 0xa9, 0x02, 0x8d, 0x0b, 0x49, 102 | 0xa9, 0x0a, 0x8d, 0x0c, 0x49, 0xa9, 0x23, 0x8d, 0x0d, 0x49, 0xa9, 0x02, 0x8d, 0x0b, 0x49, 0x60, 103 | }; 104 | std::memcpy(rom.get_buffer(), code, sizeof(code)); 105 | 106 | // Hook up IRQ interrupt handler 107 | rom.get_buffer()[kVecIRQ - kAddrROM] = 0x2E; 108 | rom.get_buffer()[kVecIRQ - kAddrROM + 1] = 0x49; 109 | 110 | CPU cpu(&bus); 111 | bus.attach_cpu(&cpu); 112 | 113 | std::thread cpu_thread([&]() { 114 | cpu.dump_state(std::cout); 115 | cpu.start(); 116 | std::cout << "cpu halted" << std::endl; 117 | cpu.dump_state(std::cout); 118 | }); 119 | 120 | io.start(); 121 | io.stop(); 122 | cpu_thread.join(); 123 | 124 | return 0; 125 | } 126 | -------------------------------------------------------------------------------- /examples/c/pong_oc_annotated.asm: -------------------------------------------------------------------------------- 1 | .RST 2 | $4920 78 SEI 3 | $4921 20 79 49 JSR $4979 4 | $4924 a9 10 LDA #$10 5 | $4926 8d 06 49 STA $4906 6 | $4929 58 CLI 7 | 8 | .L_MAIN_LOOP 9 | $492a ea NOP 10 | $492b 4c 2a 49 JMP $492a 11 | 12 | .L_IRQ 13 | $492e ad 03 49 LDA $4903 14 | $4931 c9 01 CMP #$01 15 | $4933 f0 0b BEQ $4940 16 | $4935 c9 06 CMP #$06 17 | $4937 f0 0d BEQ $4946 18 | $4939 c9 08 CMP #$08 19 | $493b f0 0f BEQ $494c 20 | $493d 4c 52 49 JMP $4952 21 | 22 | .L_IRQ_KEYDOWM 23 | $4940 20 83 49 JSR $4983 24 | $4943 4c 52 49 JMP $4952 25 | 26 | .L_IRQ_CLOCK 27 | $4946 20 d4 49 JSR $49d4 28 | $4949 4c 52 49 JMP $4952 29 | 30 | .L_IRQ_TIMER 31 | $494c 20 73 49 JSR $4973 32 | $494f 4c 52 49 JMP $4952 33 | 34 | .L_IRQ_EXIT 35 | $4952 40 RTI 36 | 37 | .L_START_SHORT_BEEP 38 | $4953 a9 d9 LDA #$d9 39 | $4955 8d 08 49 STA $4908 40 | $4958 a9 00 LDA #$00 41 | $495a 8d 11 49 STA $4911 42 | $495d a9 0a LDA #$0a 43 | $495f 8d 10 49 STA $4910 44 | $4962 60 RTS 45 | 46 | .L_START_LONG_BEEP 47 | $4963 a9 d9 LDA #$d9 48 | $4965 8d 08 49 STA $4908 49 | $4968 a9 00 LDA #$00 50 | $496a 8d 11 49 STA $4911 51 | $496d a9 1e LDA #$1e 52 | $496f 8d 10 49 STA $4910 53 | $4972 60 RTS 54 | 55 | .L_STOP_BEEP 56 | $4973 a9 19 LDA #$19 57 | $4975 8d 08 49 STA $4908 58 | $4978 60 RTS 59 | 60 | .L_RESET_GAME 61 | $4979 a9 00 LDA #$00 62 | $497b 85 02 STA $02 63 | $497d 85 03 STA $03 64 | $497f 20 f0 49 JSR $49f0 65 | $4982 60 RTS 66 | 67 | .L_HANDLE_KEYPRESS 68 | $4983 ad 04 49 LDA $4904 69 | $4986 85 08 STA $08 70 | $4988 c9 16 CMP #$16 71 | $498a f0 0f BEQ $499b 72 | $498c c9 12 CMP #$12 73 | $498e f0 19 BEQ $49a9 74 | $4990 c9 08 CMP #$08 75 | $4992 f0 23 BEQ $49b7 76 | $4994 c9 0a CMP #$0a 77 | $4996 f0 2d BEQ $49c5 78 | $4998 4c d3 49 JMP $49d3 79 | 80 | .L_HANDLE_KEYPRESS_W 81 | $499b a5 00 LDA $00 82 | $499d c9 00 CMP #$00 83 | $499f f0 32 BEQ $49d3 84 | $49a1 18 CLC 85 | $49a2 e9 01 SBC #$01 86 | $49a4 85 00 STA $00 87 | $49a6 4c d3 49 JMP $49d3 88 | 89 | .L_HANDLE_KEYPRESS_S 90 | $49a9 a5 00 LDA $00 91 | $49ab c9 1b CMP #$1b 92 | $49ad f0 24 BEQ $49d3 93 | $49af 18 CLC 94 | $49b0 69 01 ADC #$01 95 | $49b2 85 00 STA $00 96 | $49b4 4c d3 49 JMP $49d3 97 | 98 | .L_HANDLE_KEYPRESS_I 99 | $49b7 a5 01 LDA $01 100 | $49b9 c9 00 CMP #$00 101 | $49bb f0 16 BEQ $49d3 102 | $49bd 18 CLC 103 | $49be e9 01 SBC #$01 104 | $49c0 85 01 STA $01 105 | $49c2 4c d3 49 JMP $49d3 106 | 107 | .L_HANDLE_KEYPRESS_K 108 | $49c5 a5 01 LDA $01 109 | $49c7 c9 1b CMP #$1b 110 | $49c9 f0 08 BEQ $49d3 111 | $49cb 18 CLC 112 | $49cc 69 01 ADC #$01 113 | $49ce 85 01 STA $01 114 | $49d0 4c d3 49 JMP $49d3 115 | 116 | .L_HANDLE_KEYPRESS_EXIT 117 | $49d3 60 RTS 118 | 119 | .L_HANDLE_CLOCK 120 | $49d4 20 a4 4a JSR $4aa4 121 | $49d7 20 ff 49 JSR $49ff 122 | $49da 20 31 4a JSR $4a31 123 | $49dd 20 b4 4a JSR $4ab4 124 | $49e0 20 d4 4a JSR $4ad4 125 | $49e3 20 c4 4a JSR $4ac4 126 | $49e6 20 ec 4a JSR $4aec 127 | $49e9 20 1d 4b JSR $4b1d 128 | $49ec 20 2d 4b JSR $4b2d 129 | $49ef 60 RTS 130 | 131 | .L_RESET_POSITIONS 132 | $49f0 a9 20 LDA #$20 133 | $49f2 85 04 STA $04 134 | $49f4 85 05 STA $05 135 | $49f6 a9 01 LDA #$01 136 | $49f8 85 06 STA $06 137 | $49fa a9 01 LDA #$01 138 | $49fc 85 07 STA $07 139 | $49fe 60 RTS 140 | 141 | .L_UPDATE_BALL 142 | $49ff a5 06 LDA $06 143 | $4a01 d0 0a BNE $4a0d 144 | $4a03 a5 04 LDA $04 145 | $4a05 18 CLC 146 | $4a06 e9 01 SBC #$01 147 | $4a08 85 04 STA $04 148 | $4a0a 4c 17 4a JMP $4a17 149 | 150 | .L_UPDATE_BALL_X_INC 151 | $4a0d a5 04 LDA $04 152 | $4a0f 18 CLC 153 | $4a10 69 01 ADC #$01 154 | $4a12 85 04 STA $04 155 | $4a14 4c 17 4a JMP $4a17 156 | 157 | .L_UPDATE_BALL_X_END 158 | $4a17 a5 07 LDA $07 159 | $4a19 d0 0a BNE $4a25 160 | $4a1b a5 05 LDA $05 161 | $4a1d 18 CLC 162 | $4a1e e9 01 SBC #$01 163 | $4a20 85 05 STA $05 164 | $4a22 4c 30 4a JMP $4a30 165 | 166 | .L_UPDATE_BALL_Y_INC 167 | $4a25 a5 05 LDA $05 168 | $4a27 18 CLC 169 | $4a28 69 01 ADC #$01 170 | $4a2a 85 05 STA $05 171 | $4a2c ea NOP 172 | $4a2d 4c 30 4a JMP $4a30 173 | 174 | .L_UPDATE_BALL_Y_END 175 | $4a30 60 RTS 176 | 177 | .L_CHECK_BALL_COLLISIONS 178 | $4a31 a5 05 LDA $05 179 | $4a33 c9 00 CMP #$00 180 | $4a35 d0 07 BNE $4a3e 181 | $4a37 a9 01 LDA #$01 182 | $4a39 85 07 STA $07 183 | $4a3b 20 53 49 JSR $4953 184 | 185 | .L_CHECK_BALL_COLLISIONS_NO_TOP_HIT 186 | $4a3e a5 05 LDA $05 187 | $4a40 c9 23 CMP #$23 188 | $4a42 d0 07 BNE $4a4b 189 | $4a44 a9 00 LDA #$00 190 | $4a46 85 07 STA $07 191 | $4a48 20 53 49 JSR $4953 192 | 193 | .L_CHECK_BALL_COLLISIONS_NO_BOTTOM_HIT 194 | $4a4b a5 05 LDA $05 195 | $4a4d c5 00 CMP $00 196 | $4a4f b0 16 BCS $4a67 197 | $4a51 a5 05 LDA $05 198 | $4a53 18 CLC 199 | $4a54 e5 00 SBC $00 200 | $4a56 c9 08 CMP #$08 201 | $4a58 90 0d BCC $4a67 202 | $4a5a a5 04 LDA $04 203 | $4a5c c9 02 CMP #$02 204 | $4a5e d0 07 BNE $4a67 205 | $4a60 a9 01 LDA #$01 206 | $4a62 85 06 STA $06 207 | $4a64 20 53 49 JSR $4953 208 | 209 | .L_CHECK_BALL_COLLISIONS_NO_LEFT_HIT 210 | $4a67 a5 05 LDA $05 211 | $4a69 c5 01 CMP $01 212 | $4a6b b0 16 BCS $4a83 213 | $4a6d a5 05 LDA $05 214 | $4a6f 18 CLC 215 | $4a70 e5 01 SBC $01 216 | $4a72 c9 08 CMP #$08 217 | $4a74 90 0d BCC $4a83 218 | $4a76 a5 04 LDA $04 219 | $4a78 c9 02 CMP #$02 220 | $4a7a d0 07 BNE $4a83 221 | $4a7c a9 00 LDA #$00 222 | $4a7e 85 06 STA $06 223 | $4a80 20 53 49 JSR $4953 224 | 225 | .L_CHECK_BALL_COLLISIONS_NO_RIGHT_HIT 226 | $4a83 a5 04 LDA $04 227 | $4a85 c9 00 CMP #$00 228 | $4a87 d0 0d BNE $4a96 229 | $4a89 a5 03 LDA $03 230 | $4a8b 18 CLC 231 | $4a8c 69 01 ADC #$01 232 | $4a8e 85 03 STA $03 233 | $4a90 20 5f 4b JSR $4b5f 234 | $4a93 20 63 49 JSR $4963 235 | 236 | .L_CHECK_BALL_COLLISIONS_INC_1_SCORE 237 | $4a96 a5 02 LDA $02 238 | $4a98 18 CLC 239 | $4a99 69 01 ADC #$01 240 | $4a9b 85 02 STA $02 241 | $4a9d 20 5f 4b JSR $4b5f 242 | $4aa0 20 63 49 JSR $4963 243 | 244 | .L_CHECK_BALL_COLLISIONS_EXIT 245 | $4aa3 60 RTS 246 | 247 | .L_CHECK_SCORE 248 | $4aa4 a5 02 LDA $02 249 | $4aa6 c9 0a CMP #$0a 250 | $4aa8 f0 06 BEQ $4ab0 251 | $4aaa a5 03 LDA $03 252 | $4aac c9 0a CMP #$0a 253 | $4aae f0 00 BEQ $4ab0 254 | 255 | .L_CHECK_SCORE_RESET 256 | $4ab0 20 79 49 JSR $4979 257 | 258 | .L_CHECK_SCORE_END 259 | $4ab3 60 RTS 260 | 261 | .L_SET_COLOR_BLACK 262 | $4ab4 a9 00 LDA #$00 263 | $4ab6 8d 0c 49 STA $490c 264 | $4ab9 a9 80 LDA #$80 265 | $4abb 8d 0b 49 STA $490b 266 | $4abe a9 81 LDA #$81 267 | $4ac0 8d 0b 49 STA $490b 268 | $4ac3 60 RTS 269 | 270 | .L_SET_COLOR_WHITE 271 | $4ac4 a9 ff LDA #$ff 272 | $4ac6 8d 0c 49 STA $490c 273 | $4ac9 a9 80 LDA #$80 274 | $4acb 8d 0b 49 STA $490b 275 | $4ace a9 81 LDA #$81 276 | $4ad0 8d 0b 49 STA $490b 277 | $4ad3 60 RTS 278 | 279 | .L_CLEAR_SCREEN 280 | $4ad4 a9 00 LDA #$00 281 | $4ad6 8d 0c 49 STA $490c 282 | $4ad9 8d 0d 49 STA $490d 283 | $4adc a9 40 LDA #$40 284 | $4ade 8d 0e 49 STA $490e 285 | $4ae1 a9 24 LDA #$24 286 | $4ae3 8d 0f 49 STA $490f 287 | $4ae6 a9 00 LDA #$00 288 | $4ae8 8d 0b 49 STA $490b 289 | $4aeb 60 RTS 290 | 291 | .L_DRAW_RACKETS 292 | $4aec a9 01 LDA #$01 293 | $4aee 8d 0c 49 STA $490c 294 | $4af1 8d 0e 49 STA $490e 295 | $4af4 a5 00 LDA $00 296 | $4af6 8d 0d 49 STA $490d 297 | $4af9 18 CLC 298 | $4afa 69 08 ADC #$08 299 | $4afc 8d 0f 49 STA $490f 300 | $4aff a9 03 LDA #$03 301 | $4b01 8d 0b 49 STA $490b 302 | $4b04 a9 3e LDA #$3e 303 | $4b06 8d 0c 49 STA $490c 304 | $4b09 8d 0e 49 STA $490e 305 | $4b0c a5 01 LDA $01 306 | $4b0e 8d 0d 49 STA $490d 307 | $4b11 18 CLC 308 | $4b12 69 08 ADC #$08 309 | $4b14 8d 0f 49 STA $490f 310 | $4b17 a9 03 LDA #$03 311 | $4b19 8d 0b 49 STA $490b 312 | $4b1c 60 RTS 313 | 314 | .L_DRAW_BALL 315 | $4b1d a5 04 LDA $04 316 | $4b1f 8d 0c 49 STA $490c 317 | $4b22 a5 05 LDA $05 318 | $4b24 8d 0d 49 STA $490d 319 | $4b27 a9 02 LDA #$02 320 | $4b29 8d 0b 49 STA $490b 321 | $4b2c 60 RTS 322 | 323 | .L_DRAW_SCORE 324 | $4b2d a9 00 LDA #$00 325 | $4b2f 8d 0c 49 STA $490c 326 | $4b32 a9 23 LDA #$23 327 | $4b34 8d 0d 49 STA $490d 328 | $4b37 a5 02 LDA $02 329 | $4b39 8d 0e 49 STA $490e 330 | $4b3c a9 23 LDA #$23 331 | $4b3e 8d 0f 49 STA $490f 332 | $4b41 a9 03 LDA #$03 333 | $4b43 8d 0b 49 STA $490b 334 | $4b46 a9 00 LDA #$00 335 | $4b48 8d 0c 49 STA $490c 336 | $4b4b a9 22 LDA #$22 337 | $4b4d 8d 0d 49 STA $490d 338 | $4b50 a5 03 LDA $03 339 | $4b52 8d 0e 49 STA $490e 340 | $4b55 a9 22 LDA #$22 341 | $4b57 8d 0f 49 STA $490f 342 | $4b5a a9 03 LDA #$03 343 | $4b5c 8d 0b 49 STA $490b 344 | $4b5f 60 RTS 345 | 346 | .L_RESET_BALL_POSITION 347 | $4b60 a9 20 LDA #$20 348 | $4b62 85 04 STA $04 349 | $4b64 85 05 STA $05 350 | $4b66 a9 01 LDA #$01 351 | $4b68 85 06 STA $06 352 | $4b6a a9 01 LDA #$01 353 | $4b6c 85 07 STA $07 354 | $4b6e 60 RTS 355 | -------------------------------------------------------------------------------- /examples/c/pong.asm: -------------------------------------------------------------------------------- 1 | ; Load the program at 0x4920 2 | * = $4920 3 | 4 | ; Adresses on the machine 5 | define ADDR_CONTROL $4900 6 | define ADDR_TEXT_MODE_BACKGROUND_COLOR $4901 7 | define ADDR_TEXT_MODE_FOREGROUND_COLOR $4902 8 | define ADDR_EVENT_TYPE $4903 9 | define ADDR_EVENT_ARG1 $4904 10 | define ADDR_EVENT_ARG2 $4905 11 | define ADDR_CLOCK1 $4906 12 | define ADDR_CLOCK2 $4907 13 | define ADDR_AUDIO1 $4908 14 | define ADDR_AUDIO2 $4909 15 | define ADDR_AUDIO3 $490A 16 | define ADDR_DRAW_METHOD $490B 17 | define ADDR_DRAW_ARG1 $490C 18 | define ADDR_DRAW_ARG2 $490D 19 | define ADDR_DRAW_ARG3 $490E 20 | define ADDR_DRAW_ARG4 $490F 21 | define ADDR_TIMER1_LO $4910 22 | define ADDR_TIMER1_HI $4911 23 | define ADDR_TIMER2_LO $4912 24 | define ADDR_TIMER2_HI $4913 25 | define ADDR_COUNTER1 $4914 26 | define ADDR_COUNTER2 $4915 27 | 28 | ; Various constants used when interacting with the machine 29 | define EVENT_UNSPECIFIED $00 30 | define EVENT_KEYDOWN $01 31 | define EVENT_KEYUP $02 32 | define EVENT_MOUSEMOVE $03 33 | define EVENT_MOUSEDOWN $04 34 | define EVENT_MOUSEUP $05 35 | define EVENT_CLOCK1 $06 36 | define EVENT_CLOCK2 $07 37 | define EVENT_TIMER1 $08 38 | define EVENT_TIMER2 $09 39 | define EVENT_COUNTER1 $0A 40 | define EVENT_COUNTER2 $0B 41 | define KEY_W $16 42 | define KEY_S $12 43 | define KEY_I $08 44 | define KEY_K $0A 45 | define BLUE $03 46 | define YELLOW $FC 47 | define WHITE $FF 48 | define BLACK $00 49 | define DRAW_RECTANGLE $00 50 | define DRAW_SQUARE $01 51 | define DRAW_DOT $02 52 | define DRAW_LINE $03 53 | define BRUSH_SET_BODY $80 54 | define BRUSH_SET_OUTLINE $81 55 | define RACKET_WIDTH 1 56 | define RACKET_HEIGHT 8 57 | define RACKET1_X 1 58 | define RACKET2_X 62 59 | define BALL_DIAMETER 1 60 | define SCREEN_HEIGHT 36 61 | define SCREEN_WIDTH 64 62 | define RACKET_INITIAL_Y 14 63 | define BALL_INITIAL_X 18 64 | define BALL_INITIAL_Y 32 65 | define WINNING_SCORE 10 66 | define BEEP_SHORT_AUDIO $D9 67 | define BEEP_LONG_AUDIO $D3 68 | define SILENT_AUDIO $19 69 | define RACKETY_MAX 28 70 | define SCREEN_HEIGHT_M1 35 71 | define SCREEN_HEIGHT_M2 34 72 | define SCREEN_WIDTH_M1 63 73 | define RACKET1_X_P1 2 74 | define RACKET2_X_M1 61 75 | define BALL_UPDATE_CLOCK $10 76 | 77 | ; Memory locations storing program variables 78 | define V_RACKET1_POS $00 79 | define V_RACKET2_POS $01 80 | define V_SCORE1 $02 81 | define V_SCORE2 $03 82 | define V_BALL_POS_X $04 83 | define V_BALL_POS_Y $05 84 | define V_BALL_VEL_X $06 85 | define V_BALL_VEL_Y $07 86 | define V_INT_ARGUMENT_STORAGE $08 87 | 88 | L_RST: 89 | sei 90 | jsr L_RESET_GAME 91 | lda #BALL_UPDATE_CLOCK 92 | sta ADDR_CLOCK1 93 | cli 94 | 95 | L_MAIN_LOOP: 96 | wai 97 | jmp L_MAIN_LOOP 98 | 99 | L_IRQ: 100 | lda ADDR_EVENT_TYPE 101 | cmp #EVENT_KEYDOWN 102 | beq L_IRQ_KEYDOWN 103 | cmp #EVENT_CLOCK1 104 | beq L_IRQ_CLOCK 105 | cmp #EVENT_TIMER1 106 | beq L_IRQ_TIMER 107 | jmp L_IRQ_EXIT 108 | 109 | L_IRQ_KEYDOWN: 110 | jsr L_HANDLE_KEYPRESS 111 | jmp L_IRQ_EXIT 112 | 113 | L_IRQ_CLOCK: 114 | jsr L_HANDLE_CLOCK 115 | jmp L_IRQ_EXIT 116 | 117 | L_IRQ_TIMER: 118 | jsr L_STOP_BEEP 119 | jmp L_IRQ_EXIT 120 | 121 | L_IRQ_EXIT: 122 | rti 123 | 124 | L_START_SHORT_BEEP: 125 | lda #BEEP_SHORT_AUDIO 126 | sta ADDR_AUDIO1 127 | lda #$00 128 | sta ADDR_TIMER1_HI 129 | lda #$0A 130 | sta ADDR_TIMER1_LO 131 | rts 132 | 133 | L_START_LONG_BEEP: 134 | lda #BEEP_LONG_AUDIO 135 | sta ADDR_AUDIO1 136 | lda #$00 137 | sta ADDR_TIMER1_HI 138 | lda #$1E 139 | sta ADDR_TIMER1_LO 140 | rts 141 | 142 | L_STOP_BEEP: 143 | lda #SILENT_AUDIO 144 | sta ADDR_AUDIO1 145 | rts 146 | 147 | L_RESET_GAME: 148 | lda #$00 149 | sta V_SCORE1 150 | lda #$00 151 | sta V_SCORE2 152 | jsr L_RESET_POSITIONS 153 | rts 154 | 155 | L_HANDLE_KEYPRESS: 156 | lda ADDR_EVENT_ARG1 157 | sta V_INT_ARGUMENT_STORAGE 158 | cmp #KEY_W 159 | beq L_HANDLE_KEYPRESS_W 160 | cmp #KEY_S 161 | beq L_HANDLE_KEYPRESS_S 162 | cmp #KEY_I 163 | beq L_HANDLE_KEYPRESS_I 164 | cmp #KEY_K 165 | beq L_HANDLE_KEYPRESS_K 166 | jmp L_HANDLE_KEYPRESS_EXIT 167 | 168 | L_HANDLE_KEYPRESS_W: 169 | lda V_RACKET1_POS 170 | cmp #$00 171 | beq L_HANDLE_KEYPRESS_EXIT 172 | clc 173 | sbc #$01 174 | sta V_RACKET1_POS 175 | jmp L_HANDLE_KEYPRESS_EXIT 176 | 177 | L_HANDLE_KEYPRESS_S: 178 | lda V_RACKET1_POS 179 | cmp #RACKETY_MAX 180 | beq L_HANDLE_KEYPRESS_EXIT 181 | clc 182 | adc #$01 183 | sta V_RACKET1_POS 184 | jmp L_HANDLE_KEYPRESS_EXIT 185 | 186 | L_HANDLE_KEYPRESS_I: 187 | lda V_RACKET2_POS 188 | cmp #$00 189 | beq L_HANDLE_KEYPRESS_EXIT 190 | clc 191 | sbc #$01 192 | sta V_RACKET2_POS 193 | jmp L_HANDLE_KEYPRESS_EXIT 194 | 195 | L_HANDLE_KEYPRESS_K: 196 | lda V_RACKET2_POS 197 | cmp #RACKETY_MAX 198 | beq L_HANDLE_KEYPRESS_EXIT 199 | clc 200 | adc #$01 201 | sta V_RACKET2_POS 202 | jmp L_HANDLE_KEYPRESS_EXIT 203 | 204 | L_HANDLE_KEYPRESS_EXIT: 205 | rts 206 | 207 | L_HANDLE_CLOCK: 208 | jsr L_CHECK_SCORE 209 | jsr L_UPDATE_BALL 210 | jsr L_CHECK_BALL_COLLISIONS 211 | 212 | jsr L_SET_COLOR_BLACK 213 | jsr L_CLEAR_SCREEN 214 | 215 | jsr L_SET_COLOR_WHITE 216 | jsr L_DRAW_RACKETS 217 | jsr L_DRAW_BALL 218 | jsr L_DRAW_SCORE 219 | rts 220 | 221 | L_RESET_POSITIONS: 222 | lda #RACKET_INITIAL_Y 223 | sta V_RACKET1_POS 224 | lda #RACKET_INITIAL_Y 225 | sta V_RACKET2_POS 226 | jsr L_RESET_BALL_POSITION 227 | rts 228 | 229 | L_RESET_BALL_POSITION: 230 | lda #BALL_INITIAL_Y 231 | sta V_BALL_POS_X 232 | lda #BALL_INITIAL_X 233 | sta V_BALL_POS_Y 234 | lda #$01 235 | sta V_BALL_VEL_X 236 | lda #$01 237 | sta V_BALL_VEL_Y 238 | rts 239 | 240 | L_UPDATE_BALL: 241 | lda V_BALL_VEL_X 242 | bne L_UPDATE_BALL_X_INC 243 | lda V_BALL_POS_X 244 | clc 245 | sbc #$01 246 | sta V_BALL_POS_X 247 | jmp L_UPDATE_BALL_X_END 248 | 249 | L_UPDATE_BALL_X_INC: 250 | lda V_BALL_POS_X 251 | clc 252 | adc #$01 253 | sta V_BALL_POS_X 254 | jmp L_UPDATE_BALL_X_END 255 | 256 | L_UPDATE_BALL_X_END: 257 | lda V_BALL_VEL_Y 258 | bne L_UPDATE_BALL_Y_INC 259 | lda V_BALL_POS_Y 260 | clc 261 | sbc #$01 262 | sta V_BALL_POS_Y 263 | jmp L_UPDATE_BALL_Y_END 264 | 265 | L_UPDATE_BALL_Y_INC: 266 | lda V_BALL_POS_Y 267 | clc 268 | adc #$01 269 | sta V_BALL_POS_Y 270 | nop 271 | jmp L_UPDATE_BALL_Y_END 272 | 273 | L_UPDATE_BALL_Y_END: 274 | rts 275 | 276 | L_CHECK_BALL_COLLISIONS: 277 | lda V_BALL_POS_Y 278 | cmp #$00 279 | bne L_CHECK_BALL_COLLISIONS_NO_TOP_HIT 280 | lda #$01 281 | sta V_BALL_VEL_Y 282 | jsr L_START_SHORT_BEEP 283 | 284 | L_CHECK_BALL_COLLISIONS_NO_TOP_HIT: 285 | lda V_BALL_POS_Y 286 | cmp #SCREEN_HEIGHT_M1 287 | bne L_CHECK_BALL_COLLISIONS_NO_BOTTOM_HIT 288 | lda #$00 289 | sta V_BALL_VEL_Y 290 | jsr L_START_SHORT_BEEP 291 | 292 | 293 | 294 | 295 | 296 | L_CHECK_BALL_COLLISIONS_NO_BOTTOM_HIT: 297 | lda V_BALL_POS_Y 298 | cmp V_RACKET1_POS 299 | bcc L_CHECK_BALL_COLLISIONS_NO_LEFT_HIT 300 | lda V_BALL_POS_Y 301 | clc 302 | sbc V_RACKET1_POS 303 | cmp #RACKET_HEIGHT 304 | bcs L_CHECK_BALL_COLLISIONS_NO_LEFT_HIT 305 | lda V_BALL_POS_X 306 | cmp #RACKET1_X_P1 307 | bne L_CHECK_BALL_COLLISIONS_NO_LEFT_HIT 308 | lda #$01 309 | sta V_BALL_VEL_X 310 | jsr L_START_SHORT_BEEP 311 | 312 | L_CHECK_BALL_COLLISIONS_NO_LEFT_HIT: 313 | lda V_BALL_POS_Y 314 | cmp V_RACKET2_POS 315 | bcc L_CHECK_BALL_COLLISIONS_NO_RIGHT_HIT 316 | lda V_BALL_POS_Y 317 | clc 318 | sbc V_RACKET2_POS 319 | cmp #RACKET_HEIGHT 320 | bcs L_CHECK_BALL_COLLISIONS_NO_RIGHT_HIT 321 | lda V_BALL_POS_X 322 | cmp #RACKET2_X_M1 323 | bne L_CHECK_BALL_COLLISIONS_NO_RIGHT_HIT 324 | lda #$00 325 | sta V_BALL_VEL_X 326 | jsr L_START_SHORT_BEEP 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | L_CHECK_BALL_COLLISIONS_NO_RIGHT_HIT: 335 | lda V_BALL_POS_X 336 | cmp #$00 337 | bne L_CHECK_BALL_COLLISIONS_CHECK_RIGHT_GOAL 338 | lda V_SCORE2 339 | clc 340 | adc #$01 341 | sta V_SCORE2 342 | jsr L_RESET_BALL_POSITION 343 | jsr L_START_LONG_BEEP 344 | 345 | L_CHECK_BALL_COLLISIONS_CHECK_RIGHT_GOAL: 346 | 347 | lda V_BALL_POS_X 348 | cmp #SCREEN_WIDTH_M1 349 | bne L_CHECK_BALL_COLLISIONS_EXIT 350 | 351 | lda V_SCORE1 352 | clc 353 | adc #$01 354 | sta V_SCORE1 355 | jsr L_RESET_BALL_POSITION 356 | jsr L_START_LONG_BEEP 357 | 358 | L_CHECK_BALL_COLLISIONS_EXIT: 359 | rts 360 | 361 | L_CHECK_SCORE: 362 | lda V_SCORE1 363 | cmp #$0A 364 | beq L_CHECK_SCORE_RESET 365 | lda V_SCORE2 366 | cmp #$0A 367 | beq L_CHECK_SCORE_RESET 368 | jmp L_CHECK_SCORE_END 369 | 370 | L_CHECK_SCORE_RESET: 371 | jsr L_RESET_GAME 372 | 373 | L_CHECK_SCORE_END: 374 | rts 375 | 376 | L_SET_COLOR_BLACK: 377 | lda #BLACK 378 | sta ADDR_DRAW_ARG1 379 | lda #BRUSH_SET_BODY 380 | sta ADDR_DRAW_METHOD 381 | lda #BRUSH_SET_OUTLINE 382 | sta ADDR_DRAW_METHOD 383 | rts 384 | 385 | L_SET_COLOR_WHITE: 386 | lda #WHITE 387 | sta ADDR_DRAW_ARG1 388 | lda #BRUSH_SET_BODY 389 | sta ADDR_DRAW_METHOD 390 | lda #BRUSH_SET_OUTLINE 391 | sta ADDR_DRAW_METHOD 392 | rts 393 | 394 | L_SET_COLOR_BLUE: 395 | lda #BLUE 396 | sta ADDR_DRAW_ARG1 397 | lda #BRUSH_SET_BODY 398 | sta ADDR_DRAW_METHOD 399 | lda #BRUSH_SET_OUTLINE 400 | sta ADDR_DRAW_METHOD 401 | rts 402 | 403 | L_SET_COLOR_YELLOW: 404 | lda #YELLOW 405 | sta ADDR_DRAW_ARG1 406 | lda #BRUSH_SET_BODY 407 | sta ADDR_DRAW_METHOD 408 | lda #BRUSH_SET_OUTLINE 409 | sta ADDR_DRAW_METHOD 410 | rts 411 | 412 | L_CLEAR_SCREEN: 413 | lda #$00 414 | sta ADDR_DRAW_ARG1 415 | sta ADDR_DRAW_ARG2 416 | lda #SCREEN_WIDTH 417 | sta ADDR_DRAW_ARG3 418 | lda #SCREEN_HEIGHT 419 | sta ADDR_DRAW_ARG4 420 | lda #DRAW_RECTANGLE 421 | sta ADDR_DRAW_METHOD 422 | rts 423 | 424 | L_DRAW_RACKETS: 425 | lda #RACKET1_X 426 | sta ADDR_DRAW_ARG1 427 | sta ADDR_DRAW_ARG3 428 | lda V_RACKET1_POS 429 | sta ADDR_DRAW_ARG2 430 | clc 431 | adc #RACKET_HEIGHT 432 | sta ADDR_DRAW_ARG4 433 | lda #DRAW_LINE 434 | sta ADDR_DRAW_METHOD 435 | 436 | lda #RACKET2_X 437 | sta ADDR_DRAW_ARG1 438 | sta ADDR_DRAW_ARG3 439 | lda V_RACKET2_POS 440 | sta ADDR_DRAW_ARG2 441 | clc 442 | adc #RACKET_HEIGHT 443 | sta ADDR_DRAW_ARG4 444 | lda #DRAW_LINE 445 | sta ADDR_DRAW_METHOD 446 | rts 447 | 448 | L_DRAW_BALL: 449 | lda V_BALL_POS_X 450 | sta ADDR_DRAW_ARG1 451 | lda V_BALL_POS_Y 452 | sta ADDR_DRAW_ARG2 453 | lda #DRAW_DOT 454 | sta ADDR_DRAW_METHOD 455 | rts 456 | 457 | L_DRAW_SCORE: 458 | 459 | jsr L_SET_COLOR_YELLOW 460 | 461 | lda #$00 462 | sta ADDR_DRAW_ARG1 463 | lda #SCREEN_HEIGHT_M1 464 | sta ADDR_DRAW_ARG2 465 | lda V_SCORE1 466 | sta ADDR_DRAW_ARG3 467 | lda #SCREEN_HEIGHT_M1 468 | sta ADDR_DRAW_ARG4 469 | lda #DRAW_LINE 470 | sta ADDR_DRAW_METHOD 471 | 472 | jsr L_SET_COLOR_BLUE 473 | 474 | lda #$00 475 | sta ADDR_DRAW_ARG1 476 | lda #SCREEN_HEIGHT_M2 477 | sta ADDR_DRAW_ARG2 478 | lda V_SCORE2 479 | sta ADDR_DRAW_ARG3 480 | lda #SCREEN_HEIGHT_M2 481 | sta ADDR_DRAW_ARG4 482 | lda #DRAW_LINE 483 | sta ADDR_DRAW_METHOD 484 | 485 | jsr L_SET_COLOR_WHITE 486 | 487 | lda #$0A 488 | sta ADDR_DRAW_ARG1 489 | lda #SCREEN_HEIGHT_M2 490 | sta ADDR_DRAW_ARG2 491 | lda #DRAW_DOT 492 | sta ADDR_DRAW_METHOD 493 | lda #$0A 494 | sta ADDR_DRAW_ARG1 495 | lda #SCREEN_HEIGHT_M1 496 | sta ADDR_DRAW_ARG2 497 | lda #DRAW_DOT 498 | sta ADDR_DRAW_METHOD 499 | 500 | rts 501 | -------------------------------------------------------------------------------- /examples/c/pong.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Pong game written for the MOS 6502 Emulator Project 3 | * Author: Leonard Schütz 4 | * 5 | * This game was developed as my BMA-Project for the Berufsmaturitätsschule in Zürich. 6 | * 7 | * */ 8 | 9 | /* Important Addresses */ 10 | static constexpr uint8_t* ADDR_CONTROL = 0x4900; 11 | static constexpr uint8_t* ADDR_TEXT_MODE_BACKGROUND_COLOR = 0x4901; 12 | static constexpr uint8_t* ADDR_TEXT_MODE_FOREGROUND_COLOR = 0x4902; 13 | static constexpr uint8_t* ADDR_EVENT_TYPE = 0x4903; 14 | static constexpr uint8_t* ADDR_EVENT_ARG1 = 0x4904; 15 | static constexpr uint8_t* ADDR_EVENT_ARG2 = 0x4905; 16 | static constexpr uint8_t* ADDR_CLOCK1 = 0x4906; 17 | static constexpr uint8_t* ADDR_CLOCK2 = 0x4907; 18 | static constexpr uint8_t* ADDR_AUDIO1 = 0x4908; 19 | static constexpr uint8_t* ADDR_AUDIO2 = 0x4909; 20 | static constexpr uint8_t* ADDR_AUDIO3 = 0x490A; 21 | static constexpr uint8_t* ADDR_DRAW_METHOD = 0x490B; 22 | static constexpr uint8_t* ADDR_DRAW_ARG1 = 0x490C; 23 | static constexpr uint8_t* ADDR_DRAW_ARG2 = 0x490D; 24 | static constexpr uint8_t* ADDR_DRAW_ARG3 = 0x490E; 25 | static constexpr uint8_t* ADDR_DRAW_ARG4 = 0x490F; 26 | static constexpr uint8_t* ADDR_TIMER1_LO = 0x4910; 27 | static constexpr uint8_t* ADDR_TIMER1_HI = 0x4911; 28 | static constexpr uint8_t* ADDR_TIMER2_LO = 0x4912; 29 | static constexpr uint8_t* ADDR_TIMER2_HI = 0x4913; 30 | static constexpr uint8_t* ADDR_COUNTER1 = 0x4914; 31 | static constexpr uint8_t* ADDR_COUNTER2 = 0x4915; 32 | 33 | /* Event types */ 34 | static constexpr uint8_t EVENT_UNSPECIFIED = 0x00; 35 | static constexpr uint8_t EVENT_KEYDOWN = 0x01; 36 | static constexpr uint8_t EVENT_KEYUP = 0x02; 37 | static constexpr uint8_t EVENT_MOUSEMOVE = 0x03; 38 | static constexpr uint8_t EVENT_MOUSEDOWN = 0x04; 39 | static constexpr uint8_t EVENT_MOUSEUP = 0x05; 40 | static constexpr uint8_t EVENT_CLOCK1 = 0x06; 41 | static constexpr uint8_t EVENT_CLOCK2 = 0x07; 42 | static constexpr uint8_t EVENT_TIMER1 = 0x08; 43 | static constexpr uint8_t EVENT_TIMER2 = 0x09; 44 | static constexpr uint8_t EVENT_COUNTER1 = 0x0A; 45 | static constexpr uint8_t EVENT_COUNTER2 = 0x0B; 46 | 47 | /* Keycodes returned by the keyboard interrupt */ 48 | static constexpr uint8_t KEY_W = 0x16; 49 | static constexpr uint8_t KEY_S = 0x12; 50 | static constexpr uint8_t KEY_I = 0x08; 51 | static constexpr uint8_t KEY_K = 0x0A; 52 | 53 | /* Colors used for drawing on the screen */ 54 | static constexpr uint8_t BLUE = 0x03; 55 | static constexpr uint8_t YELLOW = 0xFC; 56 | static constexpr uint8_t WHITE = 0xFF; 57 | static constexpr uint8_t BLACK = 0x00; 58 | 59 | /* Constants for the IO chip drawing API */ 60 | static constexpr uint8_t DRAW_RECTANGLE = 0x00; 61 | static constexpr uint8_t DRAW_SQUARE = 0x01; 62 | static constexpr uint8_t DRAW_DOT = 0x02; 63 | static constexpr uint8_t DRAW_LINE = 0x03; 64 | static constexpr uint8_t BRUSH_SET_BODY = 0x80; 65 | static constexpr uint8_t BRUSH_SET_OUTLINE = 0x81; 66 | 67 | /* Constants for the game */ 68 | static constexpr uint8_t RACKET_WIDTH = 1; 69 | static constexpr uint8_t RACKET_HEIGHT = 8; 70 | static constexpr uint8_t RACKET1_X = 1; 71 | static constexpr uint8_t RACKET2_X = 62; 72 | static constexpr uint8_t BALL_DIAMETER = 1; 73 | static constexpr uint8_t SCREEN_HEIGHT = 36; 74 | static constexpr uint8_t SCREEN_WIDTH = 64; 75 | static constexpr uint8_t RACKET_INITIAL_Y = 14; 76 | static constexpr uint8_t BALL_INITIAL_X = 18; 77 | static constexpr uint8_t BALL_INITIAL_Y = 32; 78 | static constexpr uint8_t WINNING_SCORE = 0x0A; 79 | static constexpr uint8_t BEEP_SHORT_AUDIO = 0xD9; 80 | static constexpr uint8_t BEEP_LONG_AUDIO = 0xE3; 81 | static constexpr uint8_t SILENT_AUDIO = 0x19; 82 | 83 | static constexpr uint8_t RACKETY_MAX = 27; 84 | static constexpr uint8_t SCREEN_HEIGHT_M1 = 35; 85 | static constexpr uint8_t SCREEN_HEIGHT_M2 = 34; 86 | static constexpr uint8_t RACKET1_X_P1 = 2; 87 | static constexpr uint8_t RACKET2_X_M1 = 61; 88 | 89 | /* Timing & Speed control */ 90 | static constexpr uint8_t BALL_UPDATE_CLOCK = 0x10; 91 | 92 | /* Runtime data used by the game */ 93 | /* 0x00 */ uint8_t racket1_pos; // the y offset of the left racket 94 | /* 0x01 */ uint8_t racket2_pos; // the y offset of the right racket 95 | /* 0x02 */ uint8_t score1; // the score of the left player 96 | /* 0x03 */ uint8_t score2; // the score of the right player 97 | /* 0x04 */ uint8_t ball_pos_x; // the x position of the ball 98 | /* 0x05 */ uint8_t ball_pos_y; // the y position of the ball 99 | /* 0x06 */ uint8_t ball_vel_x; // wether the ball is moving to the right 100 | /* 0x07 */ uint8_t ball_vel_y; // wether the ball is moving downwards 101 | /* 0x08 */ uint8_t int_argument_storage; // memory to store interrupt event arguments in 102 | 103 | /* Sets up the initial state of the game and then goes to sleep */ 104 | int main() { 105 | disable_interrupts(); 106 | reset_game(); 107 | *ADDR_CLOCK1 = BALL_UPDATE_CLOCK; 108 | enable_interrupts(); 109 | 110 | // Infinite loop as we don't need this method anymore 111 | // This can be implemented using a WAI instruction. 112 | label: LOOP 113 | nop(); 114 | goto LOOP; 115 | } 116 | 117 | /* Handle IRQ interrupts by the IO chip */ 118 | int irq() { 119 | switch (*ADDR_EVENT_TYPE) { 120 | case EVENT_KEYDOWN: { 121 | handle_keypress(); 122 | break; 123 | } 124 | case EVENT_CLOCK1: { 125 | handle_clock(); 126 | break; 127 | } 128 | case EVENT_TIMER1: { 129 | stop_beep(); 130 | break; 131 | } 132 | } 133 | } 134 | 135 | void start_short_beep() { 136 | *ADDR_AUDIO1 = BEEP_SHORT_AUDIO; 137 | *ADDR_TIMER1_HI = 0x00; 138 | *ADDR_TIMER1_LO = 0x0A; // 0x0A -> 10 -> 100ms 139 | } 140 | 141 | void start_long_beep() { 142 | *ADDR_AUDIO1 = BEEP_LONG_AUDIO; 143 | *ADDR_TIMER1_HI = 0x00; 144 | *ADDR_TIMER1_LO = 0x1E; // 0x1E -> 30 -> 300ms 145 | } 146 | 147 | void stop_beep() { 148 | *ADDR_AUDIO1 = SILENT_AUDIO; 149 | } 150 | 151 | /* Resets the game after a player gained a point */ 152 | void reset_game() { 153 | score1 = 0; 154 | score2 = 0; 155 | reset_positions(); 156 | } 157 | 158 | /* Handles a keypress interrupt event */ 159 | void handle_keypress() { 160 | int_argument_storage = *ADDR_EVENT_ARG1; 161 | 162 | switch (int_argument_storage) { 163 | case KEY_W: { 164 | if (racket1_pos == 0) 165 | break; 166 | racket1_pos--; 167 | break; 168 | } 169 | case KEY_S: { 170 | if (racket1_pos == SCREEN_HEIGHT - RACKET_HEIGHT) 171 | break; 172 | racket1_pos++; 173 | break; 174 | } 175 | case KEY_I: { 176 | if (racket2_pos == 0) 177 | break; 178 | racket2_pos--; 179 | break; 180 | } 181 | case KEY_K: { 182 | if (racket2_pos == SCREEN_HEIGHT - RACKET_HEIGHT) 183 | break; 184 | racket2_pos++; 185 | break; 186 | } 187 | } 188 | } 189 | 190 | /* Handles a clock interrupt event */ 191 | void handle_clock() { 192 | // Game logic 193 | check_score(); 194 | update_ball(); 195 | check_ball_collisions(); 196 | 197 | // Clear screen 198 | set_color_black(); 199 | clear_screen(); 200 | 201 | // Render game elements 202 | set_color_white(); 203 | draw_rackets(); 204 | draw_ball(); 205 | draw_score(); 206 | } 207 | 208 | void reset_positions() { 209 | racket1_pos = RACKET_INITIAL_Y; 210 | racket2_pos = RACKET_INITIAL_Y; 211 | reset_ball_position(); 212 | } 213 | 214 | void reset_ball_position() { 215 | ball_pos_x = BALL_INITIAL_X; 216 | ball_pos_y = BALL_INITIAL_Y; 217 | ball_vel_x = true; 218 | ball_vel_y = true; 219 | } 220 | 221 | void update_ball() { 222 | // Move into the x direction 223 | if (ball_vel_x) { 224 | ball_pos_x++; 225 | } else { 226 | ball_pos_x--; 227 | } 228 | 229 | // Move into the y direction 230 | if (ball_vel_y) { 231 | ball_pos_y++; 232 | } else { 233 | ball_pos_y--; 234 | } 235 | } 236 | 237 | void check_ball_collisions() { 238 | // Check if we hit the top or bottom walls 239 | if (ball_pos_y == 0) 240 | ball_vel_y = true; 241 | if (ball_pos_y == SCREEN_HEIGHT - 1) 242 | ball_vel_y = false; 243 | 244 | // Check if we hit the left racket 245 | if (ball_pos_y >= racket1_pos && ball_pos_y < racket1_pos + RACKET_HEIGHT) { 246 | if (ball_pos_x == RACKET1_X + 1) { 247 | ball_vel_x = true; 248 | start_short_beep(); 249 | } 250 | } 251 | 252 | // Check if we hit the right racket 253 | if (ball_pos_y >= racket2_pos && ball_pos_y < racket2_pos + RACKET_HEIGHT) { 254 | if (ball_pos_x == RACKET2_X - 1) { 255 | ball_vel_x = false; 256 | start_short_beep(); 257 | } 258 | } 259 | 260 | // Check if we hit the goal of either side 261 | if (ball_pos_x == 0) { 262 | score2++; 263 | reset_ball_position(); 264 | start_long_beep(); 265 | } else if (ball_pos_x == SCREEN_WIDTH - 1) { 266 | score1++; 267 | reset_ball_position(); 268 | start_long_beep(); 269 | } 270 | } 271 | 272 | /* Check if a player won the game */ 273 | void check_score() { 274 | if (score1 == WINNING_SCORE) { 275 | reset_game(); 276 | } 277 | if (score2 == WINNING_SCORE) { 278 | reset_game(); 279 | } 280 | } 281 | 282 | /* Set the brush color to black */ 283 | void set_color_black() { 284 | *ADDR_DRAW_ARG1 = BLACK; 285 | *ADDR_DRAW_METHOD = BRUSH_SET_BODY; 286 | *ADDR_DRAW_METHOD = BRUSH_SET_OUTLINE; 287 | } 288 | 289 | /* Set the brush color to white */ 290 | void set_color_white() { 291 | *ADDR_DRAW_ARG1 = WHITE; 292 | *ADDR_DRAW_METHOD = BRUSH_SET_BODY; 293 | *ADDR_DRAW_METHOD = BRUSH_SET_OUTLINE; 294 | } 295 | 296 | /* Set the brush color to white */ 297 | void set_color_blue() { 298 | *ADDR_DRAW_ARG1 = BLUE; 299 | *ADDR_DRAW_METHOD = BRUSH_SET_BODY; 300 | *ADDR_DRAW_METHOD = BRUSH_SET_OUTLINE; 301 | } 302 | 303 | /* Set the brush color to white */ 304 | void set_color_yellow() { 305 | *ADDR_DRAW_ARG1 = YELLOW; 306 | *ADDR_DRAW_METHOD = BRUSH_SET_BODY; 307 | *ADDR_DRAW_METHOD = BRUSH_SET_OUTLINE; 308 | } 309 | 310 | /* Draws a black rectangle over the whole screen */ 311 | void clear_screen() { 312 | *ADDR_DRAW_ARG1 = 0; 313 | *ADDR_DRAW_ARG2 = 0; 314 | *ADDR_DRAW_ARG3 = SCREEN_WIDTH; 315 | *ADDR_DRAW_ARG4 = SCREEN_HEIGHT; 316 | *ADDR_DRAW_METHOD = DRAW_RECTANGLE; 317 | } 318 | 319 | /* Draw the rackets on the screen */ 320 | void draw_rackets() { 321 | // Draw racket #1 322 | *ADDR_DRAW_ARG1 = RACKET1_X; 323 | *ADDR_DRAW_ARG2 = racket1_pos; 324 | *ADDR_DRAW_ARG3 = RACKET1_X; 325 | *ADDR_DRAW_ARG4 = racket1_pos + RACKET_HEIGHT; 326 | *ADDR_DRAW_METHOD = DRAW_LINE; 327 | 328 | // Draw racket #2 329 | *ADDR_DRAW_ARG1 = RACKET2_X; 330 | *ADDR_DRAW_ARG2 = racket2_pos; 331 | *ADDR_DRAW_ARG3 = RACKET2_X; 332 | *ADDR_DRAW_ARG4 = racket2_pos + RACKET_HEIGHT; 333 | *ADDR_DRAW_METHOD = DRAW_LINE; 334 | } 335 | 336 | void draw_ball() { 337 | *ADDR_DRAW_ARG1 = ball_pos_x; 338 | *ADDR_DRAW_ARG2 = ball_pos_y; 339 | *ADDR_DRAW_METHOD = DRAW_DOT; 340 | } 341 | 342 | void draw_score() { 343 | // Draw score #1 344 | set_color_yellow(); 345 | *ADDR_DRAW_ARG1 = 0; 346 | *ADDR_DRAW_ARG2 = SCREEN_HEIGHT - 1; 347 | *ADDR_DRAW_ARG3 = score1; 348 | *ADDR_DRAW_ARG4 = SCREEN_HEIGHT - 1; 349 | *ADDR_DRAW_METHOD = DRAW_LINE; 350 | 351 | // Draw score #2 352 | set_color_blue(); 353 | *ADDR_DRAW_ARG1 = SCREEN_WIDTH - 1; 354 | *ADDR_DRAW_ARG2 = SCREEN_HEIGHT - 1; 355 | *ADDR_DRAW_ARG3 = SCREEN_WIDTH - 1 - score1; 356 | *ADDR_DRAW_ARG4 = SCREEN_HEIGHT - 1; 357 | *ADDR_DRAW_METHOD = DRAW_LINE; 358 | 359 | // Draw a vertical bar at index 10 360 | set_color_white(); 361 | *ADDR_DRAW_ARG1 = 10; 362 | *ADDR_DRAW_ARG2 = SCREEN_HEIGHT - 2; 363 | *ADDR_DRAW_METHOD = DRAW_DOT; 364 | 365 | *ADDR_DRAW_ARG1 = 10; 366 | *ADDR_DRAW_ARG2 = SCREEN_HEIGHT - 1; 367 | *ADDR_DRAW_METHOD = DRAW_DOT; 368 | } 369 | -------------------------------------------------------------------------------- /include/iochip.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the MOS 6502 Emulator 3 | * (https://github.com/KCreate/mos6502) 4 | * 5 | * MIT License 6 | * 7 | * Copyright (c) 2017 - 2018 Leonard Schütz 8 | * 9 | * Permission is hereby granted, free of charge, to any person obtaining a copy 10 | * of this software and associated documentation files (the "Software"), to deal 11 | * in the Software without restriction, including without limitation the rights 12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | * copies of the Software, and to permit persons to whom the Software is 14 | * furnished to do so, subject to the following conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be included in 17 | * all copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 25 | * SOFTWARE. 26 | */ 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | 40 | #include "busdevice.h" 41 | 42 | #pragma once 43 | 44 | namespace M6502 { 45 | 46 | // IOChip 47 | // 48 | // The virtual display shows a 64x36 window, where each pixel is directly addressable 49 | // in memory. 50 | // 51 | // Graphics mode: 52 | // In graphics mode, the display can be driven via RGB values. Each pixel is 1 byte in size and can thus 53 | // display 256 different colors. 54 | // 55 | // RGB-Pixel: rrrgggbb 56 | // 57 | // The background and foreground color configuration bytes have no effect in this mode and are ignored. 58 | // 59 | // Text mode: 60 | // In text mode, each byte of VRAM stores a character to be rendered on the display. The character set used is 61 | // plain old ASCII. Character codes above 0x7f (bytes where bit 7 is set) are interpreted as 0x00 - 0x7f but are 62 | // rendered with the background and foreground colors swapped. Background and foreground colors of the display 63 | // can be configured at their respective control ports. The color encodings of these values are defined via the 64 | // respective flag in the control byte of the IO chip. 65 | // 66 | // Character: 0 0000000 67 | // ^ ^ 68 | // | | 69 | // | +- ANSCII Character code 70 | // +--- Swap background and foreground color 71 | // 72 | // Keyboard & Mouse access: 73 | // The IOChip listens for keyboard and mouse events. These events are passed to the CPU via the interrupt mechanism. 74 | // Interrupts for either keyboard or mouse events can be disabled via their respective flags in the control byte. 75 | static std::string kIOVideoTitle = "6502 Microcontroller"; 76 | static constexpr size_t kIOVideoWidth = 64; 77 | static constexpr size_t kIOVideoHeight = 36; 78 | static constexpr size_t kIOVideoScaleWidth = 17; 79 | static constexpr size_t kIOVideoScaleHeight = 17; 80 | static constexpr size_t kIOVideoModeWidth = kIOVideoWidth * kIOVideoScaleWidth; 81 | static constexpr size_t kIOVideoModeHeight = kIOVideoHeight * kIOVideoScaleHeight; 82 | static constexpr size_t kIOVRAMSize = kIOVideoWidth * kIOVideoHeight; 83 | static constexpr size_t kIOVideoRedMask = 0xE0; 84 | static constexpr size_t kIOVideoGreenMask = 0x1C; 85 | static constexpr size_t kIOVideoBlueMask = 0x03; 86 | 87 | // Control ports 88 | // 89 | // These are the most important interfaces to the IO chip. 90 | static constexpr uint16_t kIOControl = 0x900; 91 | static constexpr uint16_t kIOTextModeBackgroundColor = 0x901; 92 | static constexpr uint16_t kIOTextModeForegroundColor = 0x902; 93 | 94 | // Event payload data 95 | // 96 | // Interrupts store their payload in these memory locations 97 | // 98 | // The keyboard modifier byte is laid out as follows: 99 | // 100 | // Modifier: 0000 0 0 0 0 101 | // ^ ^ ^ ^ ^ 102 | // | | | | +- Alt key pressed? 103 | // | | | +--- Control key pressed? 104 | // | | +----- Shift key pressed? 105 | // | +------- System key pressed? 106 | // +------------ Unused bits 107 | static constexpr uint16_t kIOEventType = 0x903; 108 | static constexpr uint16_t kIOKeyboardKeycode = 0x904; 109 | static constexpr uint16_t kIOKeyboardModifiers = 0x905; 110 | static constexpr uint16_t kIOMouseXCoord = 0x904; 111 | static constexpr uint16_t kIOMouseYCoord = 0x905; 112 | 113 | // Masks for the keyboard modifier byte 114 | static constexpr uint8_t kIOKeyboardModifierAlt = 0x01; 115 | static constexpr uint8_t kIOKeyboardModifierControl = 0x02; 116 | static constexpr uint8_t kIOKeyboardModifierShift = 0x04; 117 | static constexpr uint8_t kIOKeyboardModifierSystem = 0x08; 118 | 119 | // Hardware clocks 120 | // 121 | // The hardware clocks can interrupt the CPU at configurable intervals of time. When the value stored in these 122 | // memory locations is 0, the clocks are turned off. The minimum amount of time between clock pulses is 123 | // 5ms * f, where f is the value stored inside memory. 124 | // 125 | // Each clock interrupt sets an event type in the event type memory location so the CPU knows which clock fired. 126 | // 127 | // Clock: 00000000 128 | // ^ 129 | // | 130 | // +- Amount of 5ms pauses 131 | static constexpr uint16_t kIOClock1 = 0x906; 132 | static constexpr uint16_t kIOClock2 = 0x907; 133 | 134 | // Audio channels 135 | // 136 | // The audio system of the IO chip consists of four distinct audio channels which can each play a configurable 137 | // sound. 138 | // 139 | // AudioChannel: 00 00 0000 140 | // ^ ^ ^ 141 | // | | | 142 | // | | +- Pitch (Range of [0.2 - 2.2]) 143 | // | +---- Wave function (sine, square, saw, triangle) 144 | // +------- Volume (0%, 25%, 50%, 100%) 145 | // 146 | // Setting the volume to 0 will stop playing the audio, so no resources are wasted playing a mute sound. 147 | static constexpr uint16_t kIOAudioChannel1 = 0x908; 148 | static constexpr uint16_t kIOAudioChannel2 = 0x909; 149 | static constexpr uint16_t kIOAudioChannel3 = 0x90A; 150 | 151 | // Masks for the audio channel 152 | static constexpr uint8_t kIOAudioChannelVolume = 0xC0; 153 | static constexpr uint8_t kIOAudioChannelWave = 0x30; 154 | static constexpr uint8_t kIOAudioChannelPitch = 0x0F; 155 | enum { 156 | kIOAudioChannelVolume0 = 0, 157 | kIOAudioChannelVolume25 = 1, 158 | kIOAudioChannelVolume50 = 2, 159 | kIOAudioChannelVolume100 = 3, 160 | kIOAudioChannelWaveSine = 0, 161 | kIOAudioChannelWaveSquare = 1, 162 | kIOAudioChannelWaveSaw = 2, 163 | kIOAudioChannelWaveTriangle = 3, 164 | }; 165 | 166 | // Some constants related to how audio data is stored inside the chip 167 | static constexpr uint32_t kIOAudioSamples = 44100; 168 | static constexpr uint32_t kIOAudioSampleRate = 44100; 169 | static constexpr uint32_t kIOAudioAmplitude = 30000; 170 | static constexpr double kIOAudioTwoPi = 6.28318; 171 | static constexpr double kIOAudioSampleIncrement = 440.0 / 44100; 172 | 173 | // VRAM Access 174 | // 175 | // There are two methods of directly writing to VRAM. You either write directly to an address in the VRAM, 176 | // (e.g. through some 16-bit addressing mode) or you utilize the various drawing functions the IOChip provides. 177 | // 178 | // Writing to kIODrawMethod will commit the action and execute the corresponding method. Values inside the argument 179 | // bytes will not be altered and can be used for further drawing. 180 | // 181 | // Drawing functions supported by the IOChip: 182 | // 183 | // Geometric shapes 184 | // io_draw_rectangle(x, y, w, h) 185 | // io_draw_square(x, y, s) 186 | // io_draw_dot(x, y) - Uses the body color 187 | // io_draw_line(x1, y2, x2, y2) 188 | // 189 | // Color 190 | // io_brush_set_body(v) 191 | // io_brush_set_outline(v) 192 | static constexpr uint16_t kIODrawMethod = 0x90B; 193 | static constexpr uint16_t kIODrawArg1 = 0x90C; 194 | static constexpr uint16_t kIODrawArg2 = 0x90D; 195 | static constexpr uint16_t kIODrawArg3 = 0x90E; 196 | static constexpr uint16_t kIODrawArg4 = 0x90F; 197 | 198 | // Draw method codes 199 | static constexpr uint8_t kIODrawRectangle = 0x00; 200 | static constexpr uint8_t kIODrawSquare = 0x01; 201 | static constexpr uint8_t kIODrawDot = 0x02; 202 | static constexpr uint8_t kIODrawLine = 0x03; 203 | static constexpr uint8_t kIOBrushSetBody = 0x80; 204 | static constexpr uint8_t kIOBrushSetOutline = 0x81; 205 | 206 | // Writing to this memory locations will start a timer, which fires an IRQ interrupt 207 | // after the specified amount of time. The value inside the two bytes is read as a 16-bit 208 | // value. The value is multiplied with 10, resulting in the amount of milliseconds that 209 | // should pass. Writing to the Lo byte will trigger the timer. Writing to the Hi byte 210 | // only has no effect. The memory won't be altered at any phase in the timer. 211 | // 212 | // A timer cannot be cancelled once activated. 213 | // 214 | // timer: 00000000 215 | // ^ 216 | // +- Value in 10ms 217 | static constexpr uint16_t kIOTimer1Lo = 0x910; 218 | static constexpr uint16_t kIOTimer1Hi = 0x911; 219 | static constexpr uint16_t kIOTimer2Lo = 0x912; 220 | static constexpr uint16_t kIOTimer2Hi = 0x913; 221 | 222 | // Similar to the timers mentioned above, these memory locations can be used to fire 223 | // a timer. Unlike the regular timers, these will fire in 1 second intervals, decrementing 224 | // the value stored in memory until it has reached 0. 225 | // 226 | // A counter can be cancelled while running by writing 0 to memory. 227 | // 228 | // counter: 00000000 229 | // ^ 230 | // +- Value in seconds 231 | static constexpr uint16_t kIOCounter1 = 0x914; 232 | static constexpr uint16_t kIOCounter2 = 0x915; 233 | 234 | // Reserved for future expansion 235 | static constexpr uint16_t kIOReserved7 = 0x916; 236 | static constexpr uint16_t kIOReserved8 = 0x917; 237 | static constexpr uint16_t kIOReserved9 = 0x918; 238 | static constexpr uint16_t kIOReserved10 = 0x919; 239 | static constexpr uint16_t kIOReserved11 = 0x91A; 240 | static constexpr uint16_t kIOReserved12 = 0x91B; 241 | static constexpr uint16_t kIOReserved13 = 0x91C; 242 | static constexpr uint16_t kIOReserved14 = 0x91D; 243 | static constexpr uint16_t kIOReserved15 = 0x91E; 244 | static constexpr uint16_t kIOReserved16 = 0x91F; 245 | 246 | // Interrupt event codes 247 | // 248 | // When the IO chip interrupts the CPU and requests servicing, it puts an event type into the respective memory 249 | // location. The Chip can then decide what to do with the data. 250 | static constexpr uint8_t kIOEventUnspecified = 0x00; 251 | static constexpr uint8_t kIOEventKeydown = 0x01; 252 | static constexpr uint8_t kIOEventKeyup = 0x02; 253 | static constexpr uint8_t kIOEventMousemove = 0x03; 254 | static constexpr uint8_t kIOEventMousedown = 0x04; 255 | static constexpr uint8_t kIOEventMouseup = 0x05; 256 | static constexpr uint8_t kIOEventClock1 = 0x06; 257 | static constexpr uint8_t kIOEventClock2 = 0x07; 258 | static constexpr uint8_t kIOEventTimer1 = 0x08; 259 | static constexpr uint8_t kIOEventTimer2 = 0x09; 260 | static constexpr uint8_t kIOEventCounter1 = 0x0A; 261 | static constexpr uint8_t kIOEventCounter2 = 0x0B; 262 | 263 | // Misc. IO control flags 264 | // 265 | // IO + 0x900: 0 0 0 0 1 1 0 0 266 | // ^ ^ ^ ^ ^ ^ ^ ^ 267 | // | | | | | | | | 268 | // | | | | | | | +- Reserved for future expansion 269 | // | | | | | | +--- Reserved for future expansion 270 | // | | | | | +----- Enable / Disable the mouse 271 | // | | | | +------- Enable / Disable the keyboard 272 | // | | | +--------- Landscape / Portrait layout 273 | // | | +----------- Floating or Fullscreen window 274 | // | +------------- Show or hide the window 275 | // +--------------- Graphics or text mode 276 | static constexpr uint8_t kIOControlMode = 0x80; 277 | static constexpr uint8_t kIOControlVisibility = 0x40; 278 | static constexpr uint8_t kIOControlFullscreen = 0x20; 279 | static constexpr uint8_t kIOControlOrientation = 0x10; 280 | static constexpr uint8_t kIOControlKeyboardDisabled = 0x08; 281 | static constexpr uint8_t kIOControlMouseDisabled = 0x04; 282 | 283 | // 1 byte RGB value 284 | struct ColorValue { 285 | uint8_t value; 286 | 287 | ColorValue() : value(0x00) { 288 | } 289 | 290 | ColorValue(uint8_t v) : value(v) { 291 | } 292 | 293 | inline uint8_t get_r() { 294 | return ((this->value & kIOVideoRedMask) >> 5) * 32; 295 | } 296 | inline uint8_t get_g() { 297 | return ((this->value & kIOVideoGreenMask) >> 2) * 32; 298 | } 299 | inline uint8_t get_b() { 300 | return (this->value & kIOVideoBlueMask) * 64; 301 | } 302 | inline sf::Color get_sfml_color() { 303 | return sf::Color(this->get_r(), this->get_g(), this->get_b()); 304 | } 305 | }; 306 | 307 | // Draw instruction telling the drawing thread what to do 308 | struct DrawInstruction { 309 | uint8_t method_code; 310 | uint8_t arg1; 311 | uint8_t arg2; 312 | uint8_t arg3; 313 | uint8_t arg4; 314 | }; 315 | 316 | // Decodes the audio channel bytes into its components 317 | class AudioChannelSettingsDecoder { 318 | public: 319 | float volume; 320 | float pitch; 321 | uint8_t wave_function; 322 | 323 | AudioChannelSettingsDecoder(uint8_t data = 0x00) { 324 | this->update(data); 325 | } 326 | 327 | AudioChannelSettingsDecoder(const AudioChannelSettingsDecoder& o) 328 | : volume(o.volume), pitch(o.pitch), wave_function(o.wave_function) { 329 | } 330 | 331 | void update(uint8_t data = 0x00) { 332 | uint8_t volume_raw = (data & kIOAudioChannelVolume) >> 6; 333 | uint8_t wave_raw = (data & kIOAudioChannelWave) >> 4; 334 | uint8_t pitch_raw = data & kIOAudioChannelPitch; 335 | 336 | this->pitch = 0.2 + (static_cast(pitch_raw) / 16) * 2; 337 | this->wave_function = wave_raw; 338 | 339 | if (volume_raw == kIOAudioChannelVolume0) 340 | this->volume = 0; 341 | if (volume_raw == kIOAudioChannelVolume25) 342 | this->volume = 25; 343 | if (volume_raw == kIOAudioChannelVolume50) 344 | this->volume = 50; 345 | if (volume_raw == kIOAudioChannelVolume100) 346 | this->volume = 100; 347 | } 348 | }; 349 | 350 | class IOChip : public BusDevice { 351 | public: 352 | IOChip(uint16_t maddr); 353 | ~IOChip(); 354 | 355 | void start(); 356 | void stop(); 357 | void write(uint16_t address, uint8_t value); 358 | uint8_t read(uint16_t address); 359 | 360 | private: 361 | // Prepares the audio buffers for the different wave functions 362 | void load_audio_buffers(); 363 | sf::SoundBuffer audio_buffer_sine; 364 | sf::SoundBuffer audio_buffer_square; 365 | sf::SoundBuffer audio_buffer_saw; 366 | sf::SoundBuffer audio_buffer_triangle; 367 | sf::Sound audio_sound1; 368 | sf::Sound audio_sound2; 369 | sf::Sound audio_sound3; 370 | AudioChannelSettingsDecoder audio_cache1; 371 | AudioChannelSettingsDecoder audio_cache2; 372 | AudioChannelSettingsDecoder audio_cache3; 373 | 374 | // Update one of the currently playing sounds 375 | void update_audio(uint16_t address, uint8_t value); 376 | 377 | void thread_clock(uint16_t clock_offset); 378 | void thread_render(); 379 | void thread_drawing(); 380 | void thread_timer(uint16_t address); 381 | void thread_counter(uint16_t address); 382 | 383 | union { 384 | uint8_t memory[0x910]; 385 | struct { 386 | uint8_t vram[0x900]; 387 | uint8_t control; 388 | ColorValue background_color; 389 | ColorValue foreground_color; 390 | uint8_t event_type; 391 | union { 392 | struct { 393 | uint8_t keycode; 394 | } keyboard; 395 | struct { 396 | uint8_t x; 397 | uint8_t y; 398 | } mouse; 399 | }; 400 | uint8_t clock1; 401 | uint8_t clock2; 402 | uint8_t audio_channel1; 403 | uint8_t audio_channel2; 404 | uint8_t audio_channel3; 405 | uint8_t draw_method; 406 | uint8_t draw_arg1; 407 | uint8_t draw_arg2; 408 | uint8_t draw_arg3; 409 | uint8_t draw_arg4; 410 | }; 411 | }; 412 | 413 | // Thread synchronisation stuff 414 | sf::RenderWindow* main_window = nullptr; 415 | std::thread render_thread; 416 | std::thread drawing_thread; 417 | std::atomic shutdown; 418 | std::vector worker_threads; 419 | 420 | // Rendering configuration 421 | std::atomic text_mode; 422 | std::atomic window_hidden; 423 | std::atomic window_fullscreen; 424 | std::atomic window_portrait; 425 | std::atomic keyboard_disabled; 426 | std::atomic mouse_disabled; 427 | 428 | // Advanced rendering 429 | std::queue draw_pipeline; 430 | std::mutex draw_mutex; 431 | std::shared_mutex draw_pipeline_mutex; 432 | std::condition_variable condition_draw; 433 | std::atomic brush_body_color; 434 | std::atomic brush_outline_color; 435 | 436 | // Advanced drawing methods 437 | void draw_rectangle(uint8_t x, uint8_t y, uint8_t w, uint8_t h); 438 | void draw_square(uint8_t x, uint8_t y, uint8_t s); 439 | void draw_dot(uint8_t x, uint8_t y); 440 | void draw_line(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2); 441 | }; 442 | 443 | } // namespace M6502 444 | -------------------------------------------------------------------------------- /src/iochip.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the MOS 6502 Emulator 3 | * (https://github.com/KCreate/mos6502) 4 | * 5 | * MIT License 6 | * 7 | * Copyright (c) 2017 - 2018 Leonard Schütz 8 | * 9 | * Permission is hereby granted, free of charge, to any person obtaining a copy 10 | * of this software and associated documentation files (the "Software"), to deal 11 | * in the Software without restriction, including without limitation the rights 12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | * copies of the Software, and to permit persons to whom the Software is 14 | * furnished to do so, subject to the following conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be included in 17 | * all copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 25 | * SOFTWARE. 26 | */ 27 | 28 | #include 29 | #include 30 | #include 31 | 32 | #ifdef LINUX 33 | #include 34 | #ifdef None 35 | #undef None 36 | #define X11None 0L 37 | #ifdef RevertToNone 38 | #undef RevertToNone 39 | #define RevertToNone (int)X11None 40 | #endif 41 | #endif 42 | #endif 43 | 44 | #include "bus.h" 45 | #include "iochip.h" 46 | 47 | namespace M6502 { 48 | 49 | IOChip::IOChip(uint16_t maddr) : BusDevice(maddr) { 50 | std::memset(this->vram, 0, kIOVRAMSize); 51 | this->control = kIOControlKeyboardDisabled | kIOControlMouseDisabled; 52 | 53 | this->background_color = ColorValue(0x00); 54 | this->foreground_color = ColorValue(0xFF); 55 | 56 | this->event_type = kIOEventUnspecified; 57 | this->mouse.x = 0x00; 58 | this->mouse.y = 0x00; 59 | 60 | this->clock1 = 0x00; 61 | this->clock2 = 0x00; 62 | 63 | this->audio_channel1 = 0x00; 64 | this->audio_channel2 = 0x00; 65 | this->audio_channel3 = 0x00; 66 | 67 | this->shutdown = false; 68 | } 69 | 70 | IOChip::~IOChip() { 71 | if (!this->shutdown) 72 | this->stop(); 73 | } 74 | 75 | void IOChip::start() { 76 | #ifdef LINUX 77 | XInitThreads(); 78 | #endif 79 | 80 | this->load_audio_buffers(); 81 | 82 | this->shutdown = false; 83 | 84 | // Start the audio, clock and rendering threads 85 | this->worker_threads.push_back(std::thread(&IOChip::thread_clock, this, kIOClock1)); 86 | this->worker_threads.push_back(std::thread(&IOChip::thread_clock, this, kIOClock2)); 87 | this->drawing_thread = std::thread(&IOChip::thread_drawing, this); 88 | 89 | // Create the window and the thread which handles all the drawing 90 | // 91 | // We separate the event loop and the drawing code because we don't want to 92 | // drop any frames just because we're waiting for events. 93 | // 94 | // SFML supports this natively and we don't have to write any new code to make 95 | // this work. 96 | // 97 | // See https://www.sfml-dev.org/tutorials/2.0/graphics-draw.php for a simple 98 | // tutorial on how drawing from other threads works. 99 | sf::VideoMode video_mode(kIOVideoModeWidth, kIOVideoModeHeight); 100 | this->main_window = new sf::RenderWindow(video_mode, kIOVideoTitle, sf::Style::Titlebar); 101 | this->render_thread = std::thread(&IOChip::thread_render, this); 102 | 103 | sf::Event event; 104 | while (!this->shutdown && this->main_window->isOpen()) { 105 | while (this->main_window->pollEvent(event)) { 106 | switch (event.type) { 107 | case sf::Event::Closed: { 108 | this->main_window->close(); 109 | this->shutdown = true; 110 | break; 111 | } 112 | case sf::Event::KeyPressed: { 113 | uint8_t modifier_byte = 0x00; 114 | if (event.key.alt) 115 | modifier_byte |= kIOKeyboardModifierAlt; 116 | if (event.key.control) 117 | modifier_byte |= kIOKeyboardModifierControl; 118 | if (event.key.shift) 119 | modifier_byte |= kIOKeyboardModifierShift; 120 | if (event.key.system) 121 | modifier_byte |= kIOKeyboardModifierSystem; 122 | this->memory[kIOEventType] = kIOEventKeydown; 123 | this->memory[kIOKeyboardKeycode] = event.key.code; 124 | this->memory[kIOKeyboardModifiers] = modifier_byte; 125 | this->bus->int_irq(); 126 | break; 127 | } 128 | default: { 129 | // TODO: Handle events 130 | break; 131 | } 132 | } 133 | } 134 | } 135 | } 136 | 137 | void IOChip::stop() { 138 | this->shutdown = true; 139 | this->render_thread.join(); 140 | this->condition_draw.notify_one(); 141 | this->drawing_thread.join(); 142 | for (auto& t : this->worker_threads) 143 | t.join(); 144 | 145 | delete this->main_window; 146 | 147 | this->worker_threads.clear(); 148 | this->main_window = nullptr; 149 | } 150 | 151 | void IOChip::load_audio_buffers() { 152 | sf::Int16 raw[kIOAudioSamples]; 153 | double x; 154 | 155 | // Generate sine wave data 156 | x = 0; 157 | for (uint32_t i = 0; i < kIOAudioSamples; i++) { 158 | raw[i] = kIOAudioAmplitude * sin(x * kIOAudioTwoPi); 159 | x += kIOAudioSampleIncrement; 160 | } 161 | this->audio_buffer_sine.loadFromSamples(raw, kIOAudioSamples, 1, kIOAudioSampleRate); 162 | 163 | // Generate square wave data 164 | x = 0; 165 | for (uint32_t i = 0; i < kIOAudioSamples; i++) { 166 | raw[i] = kIOAudioAmplitude * (sin(x * kIOAudioTwoPi) >= 0.0 ? 1 : 0.5); 167 | x += kIOAudioSampleIncrement; 168 | } 169 | this->audio_buffer_square.loadFromSamples(raw, kIOAudioSamples, 1, kIOAudioSampleRate); 170 | 171 | // TODO: Generate saw and triangle wave data 172 | // Right now we just load these buffers with square wave data 173 | this->audio_buffer_saw.loadFromSamples(raw, kIOAudioSamples, 1, kIOAudioSampleRate); 174 | this->audio_buffer_triangle.loadFromSamples(raw, kIOAudioSamples, 1, kIOAudioSampleRate); 175 | 176 | this->audio_sound1.setLoop(true); 177 | this->audio_sound2.setLoop(true); 178 | this->audio_sound3.setLoop(true); 179 | } 180 | 181 | void IOChip::thread_clock(uint16_t source) { 182 | uint8_t clock_source = 0; 183 | while (!this->shutdown) { 184 | clock_source = this->memory[source]; 185 | if (!clock_source) { 186 | std::this_thread::sleep_for(std::chrono::milliseconds(500)); 187 | continue; 188 | } 189 | std::this_thread::sleep_for(std::chrono::milliseconds(static_cast(5) * clock_source)); 190 | this->memory[kIOEventType] = source == kIOClock1 ? kIOEventClock1 : kIOEventClock2; 191 | this->bus->int_irq(); 192 | } 193 | } 194 | 195 | void IOChip::thread_drawing() { 196 | while (!this->shutdown) { 197 | std::unique_lock l(this->draw_mutex); 198 | this->condition_draw.wait(l, [&]() { 199 | std::shared_lock lk(this->draw_pipeline_mutex); 200 | return this->draw_pipeline.size() > 0 || this->shutdown; 201 | }); 202 | 203 | // Check if we should shutdown 204 | if (this->shutdown) 205 | continue; 206 | 207 | // Fetch an instruction from the pipeline 208 | DrawInstruction instruction; 209 | { 210 | std::unique_lock lk(this->draw_pipeline_mutex); 211 | instruction = this->draw_pipeline.front(); 212 | this->draw_pipeline.pop(); 213 | } 214 | 215 | switch (instruction.method_code) { 216 | case kIODrawRectangle: { 217 | this->draw_rectangle(instruction.arg1, instruction.arg2, instruction.arg3, instruction.arg4); 218 | break; 219 | } 220 | case kIODrawSquare: { 221 | this->draw_square(instruction.arg1, instruction.arg2, instruction.arg3); 222 | break; 223 | } 224 | case kIODrawDot: { 225 | this->draw_dot(instruction.arg1, instruction.arg2); 226 | break; 227 | } 228 | case kIODrawLine: { 229 | this->draw_line(instruction.arg1, instruction.arg2, instruction.arg3, instruction.arg4); 230 | break; 231 | } 232 | case kIOBrushSetBody: { 233 | this->brush_body_color = instruction.arg1; 234 | break; 235 | } 236 | case kIOBrushSetOutline: { 237 | this->brush_outline_color = instruction.arg1; 238 | break; 239 | } 240 | } 241 | } 242 | } 243 | 244 | void IOChip::thread_render() { 245 | while (!this->shutdown && this->main_window->isOpen()) { 246 | // Check the control bytes for the configuration of the display 247 | bool portrait_mode = this->control & kIOControlOrientation; 248 | bool window_hidden = this->control & kIOControlVisibility; 249 | // bool text_mode = this->control & kIOControlMode; 250 | 251 | // If the screen is hidden we sleep for some time until the window 252 | // is visible again. 253 | // 254 | // TODO: Could this be made more efficient using a lock? 255 | if (window_hidden) { 256 | std::this_thread::sleep_for(std::chrono::seconds(1)); 257 | continue; 258 | } 259 | 260 | std::this_thread::sleep_for(std::chrono::milliseconds(16)); 261 | 262 | // These are the dimensions of the window we are drawing to 263 | // and of the brush that is used to paint the pixels. 264 | uint32_t screen_width = portrait_mode ? kIOVideoHeight : kIOVideoWidth; 265 | uint32_t screen_height = portrait_mode ? kIOVideoWidth : kIOVideoHeight; 266 | uint32_t pixels_row = portrait_mode ? kIOVideoScaleHeight : kIOVideoScaleWidth; 267 | uint32_t pixels_column = portrait_mode ? kIOVideoScaleWidth : kIOVideoScaleHeight; 268 | 269 | // Iterate over every pixel in VRAM and draw it to the screen 270 | sf::RectangleShape brush(sf::Vector2f(pixels_row, pixels_column)); 271 | for (uint8_t y = 0; y < screen_height; y++) { 272 | for (uint8_t x = 0; x < screen_width; x++) { 273 | // Fetch the color from VRAM 274 | uint8_t vram_raw = this->vram[x + y * screen_width]; 275 | ColorValue vram_color(vram_raw); 276 | 277 | // Paint the corresponding section of the window 278 | brush.setFillColor(vram_color.get_sfml_color()); 279 | brush.setPosition(sf::Vector2f(x * pixels_row, y * pixels_column)); 280 | this->main_window->draw(brush); 281 | } 282 | } 283 | 284 | this->main_window->display(); 285 | } 286 | } 287 | 288 | void IOChip::write(uint16_t address, uint8_t value) { 289 | this->memory[address] = value; 290 | 291 | // Some memory locations require further processing, these are handled here 292 | // 293 | // TODO: The render thread should pause if there are no new VRAM updates. 294 | // This could be achieved via some new control flag which would enable 295 | // or disable the render thread. 296 | switch (address) { 297 | case kIOControl: { 298 | this->text_mode = (value & kIOControlMode); 299 | this->window_hidden = (value & kIOControlVisibility); 300 | this->window_fullscreen = (value & kIOControlFullscreen); 301 | this->window_portrait = (value & kIOControlOrientation); 302 | this->keyboard_disabled = (value & kIOControlKeyboardDisabled); 303 | this->mouse_disabled = (value & kIOControlMouseDisabled); 304 | break; 305 | } 306 | case kIODrawMethod: { 307 | // Load the draw instruction and append to the pipeline 308 | uint8_t arg1 = this->memory[kIODrawArg1]; 309 | uint8_t arg2 = this->memory[kIODrawArg2]; 310 | uint8_t arg3 = this->memory[kIODrawArg3]; 311 | uint8_t arg4 = this->memory[kIODrawArg4]; 312 | 313 | { 314 | std::unique_lock lk(this->draw_pipeline_mutex); 315 | this->draw_pipeline.push({value, arg1, arg2, arg3, arg4}); 316 | } 317 | 318 | // Notify the drawing thread that there is work to do 319 | this->condition_draw.notify_one(); 320 | break; 321 | } 322 | case kIOAudioChannel1: 323 | case kIOAudioChannel2: 324 | case kIOAudioChannel3: { 325 | this->update_audio(address, value); 326 | break; 327 | } 328 | case kIOTimer1Lo: 329 | case kIOTimer2Lo: { 330 | this->worker_threads.push_back(std::thread(&IOChip::thread_timer, this, address)); 331 | break; 332 | } 333 | case kIOCounter1: 334 | case kIOCounter2: { 335 | this->worker_threads.push_back(std::thread(&IOChip::thread_counter, this, address)); 336 | break; 337 | } 338 | } 339 | } 340 | 341 | uint8_t IOChip::read(uint16_t address) { 342 | return this->memory[address]; 343 | } 344 | 345 | void IOChip::update_audio(uint16_t address, uint8_t value) { 346 | // Decode 347 | AudioChannelSettingsDecoder decoder(value); 348 | 349 | // Get the source buffer for the sound 350 | sf::SoundBuffer* source_buffer = nullptr; 351 | if (decoder.wave_function == kIOAudioChannelWaveSine) 352 | source_buffer = &this->audio_buffer_sine; 353 | if (decoder.wave_function == kIOAudioChannelWaveSquare) 354 | source_buffer = &this->audio_buffer_square; 355 | if (decoder.wave_function == kIOAudioChannelWaveSaw) 356 | source_buffer = &this->audio_buffer_saw; 357 | if (decoder.wave_function == kIOAudioChannelWaveTriangle) 358 | source_buffer = &this->audio_buffer_triangle; 359 | 360 | // Get the target audio channel 361 | sf::Sound* target_channel = nullptr; 362 | AudioChannelSettingsDecoder* target_cache = nullptr; 363 | if (address == kIOAudioChannel1) { 364 | target_channel = &this->audio_sound1; 365 | target_cache = &this->audio_cache1; 366 | } 367 | if (address == kIOAudioChannel2) { 368 | target_channel = &this->audio_sound2; 369 | target_cache = &this->audio_cache1; 370 | } 371 | if (address == kIOAudioChannel3) { 372 | target_channel = &this->audio_sound3; 373 | target_cache = &this->audio_cache1; 374 | } 375 | 376 | // Update the audio channel 377 | if (decoder.volume != target_cache->volume) { 378 | if (decoder.volume == 0) { 379 | target_channel->pause(); 380 | } 381 | target_channel->setVolume(decoder.volume); 382 | } 383 | if (decoder.pitch != target_cache->pitch) { 384 | target_channel->setPitch(decoder.pitch); 385 | } 386 | if (decoder.wave_function != target_cache->wave_function) { 387 | target_channel->setBuffer(*source_buffer); 388 | } 389 | if (decoder.volume != 0) { 390 | target_channel->play(); 391 | } 392 | 393 | *target_cache = decoder; 394 | } 395 | 396 | void IOChip::thread_timer(uint16_t address) { 397 | uint8_t lo = this->memory[address]; 398 | uint8_t hi = this->memory[address + 1]; 399 | uint32_t timer_milliseconds = ((hi << 8) + lo) * 10; 400 | std::this_thread::sleep_for(std::chrono::milliseconds(timer_milliseconds)); 401 | if (!this->shutdown) { 402 | uint8_t event_type = address == kIOTimer1Lo ? kIOEventTimer1 : kIOEventTimer2; 403 | this->memory[kIOEventType] = event_type; 404 | this->bus->int_irq(); 405 | } 406 | } 407 | 408 | void IOChip::thread_counter(uint16_t address) { 409 | uint8_t seconds = this->memory[address]; 410 | while (!this->shutdown && seconds) { 411 | std::this_thread::sleep_for(std::chrono::seconds(1)); 412 | uint8_t event_type = address == kIOCounter1 ? kIOEventCounter1 : kIOEventCounter2; 413 | this->memory[kIOEventType] = event_type; 414 | this->bus->int_irq(); 415 | seconds = this->memory[address]; 416 | } 417 | } 418 | 419 | void IOChip::draw_rectangle(uint8_t x, uint8_t y, uint8_t w, uint8_t h) { 420 | bool portrait_mode = this->control & kIOControlOrientation; 421 | uint8_t screen_width = portrait_mode ? kIOVideoHeight : kIOVideoWidth; 422 | uint8_t screen_height = portrait_mode ? kIOVideoWidth : kIOVideoHeight; 423 | 424 | // Draw the rectangle 425 | for (uint8_t by = 0; by < h; by++) { 426 | if (by + y >= screen_height) 427 | continue; 428 | for (uint8_t bx = 0; bx < w; bx++) { 429 | uint8_t color = 0x00; 430 | if (by == 0 || bx == 0 || by == h - 1 || bx == w - 1) { 431 | color = this->brush_outline_color; 432 | } else { 433 | color = this->brush_body_color; 434 | } 435 | 436 | if (bx + x >= screen_width) 437 | continue; 438 | uint32_t offset = (bx + x) + (by + y) * screen_width; 439 | this->vram[offset] = color; 440 | } 441 | } 442 | } 443 | 444 | void IOChip::draw_square(uint8_t x, uint8_t y, uint8_t s) { 445 | bool portrait_mode = this->control & kIOControlOrientation; 446 | uint8_t screen_width = portrait_mode ? kIOVideoHeight : kIOVideoWidth; 447 | 448 | // Draw the rectangle 449 | for (uint8_t by = 0; by < s; by++) { 450 | for (uint8_t bx = 0; bx < s; bx++) { 451 | uint8_t color = by == 0 || bx == 0 || by == s - 1 || bx == s - 1 ? this->brush_outline_color : brush_body_color; 452 | uint32_t offset = (bx + x) + (by + y) * screen_width; 453 | if (offset >= 0x900) 454 | continue; 455 | this->vram[offset] = color; 456 | } 457 | } 458 | } 459 | 460 | void IOChip::draw_dot(uint8_t x, uint8_t y) { 461 | bool portrait_mode = this->control & kIOControlOrientation; 462 | uint8_t screen_width = portrait_mode ? kIOVideoHeight : kIOVideoWidth; 463 | uint16_t offset = x + y * screen_width; 464 | if (offset >= 0x900) 465 | return; 466 | this->vram[offset] = this->brush_body_color; 467 | } 468 | 469 | void IOChip::draw_line(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2) { 470 | 471 | //uint32_t x1_u32 = x1; 472 | //uint32_t y1_u32 = y1; 473 | //uint32_t x2_u32 = x2; 474 | //uint32_t y2_u32 = y2; 475 | //std::cout << "drawing (" << x1_u32 << ", " << y1_u32 << ") to (" << x2_u32 << ", " << y2_u32 << ")\n"; 476 | 477 | // Bresenham's line algorithm 478 | // 479 | // Source: http://rosettacode.org/wiki/Bitmap/Bresenham%27s_line_algorithm#C.2B.2B 480 | const bool steep = (std::abs(y2 - y1) > std::abs(x2 - x1)); 481 | if (steep) { 482 | std::swap(x1, y1); 483 | std::swap(x2, y2); 484 | } 485 | 486 | if (x1 > x2) { 487 | std::swap(x1, x2); 488 | std::swap(y1, y2); 489 | } 490 | 491 | const float dx = x2 - x1; 492 | const float dy = std::abs(y2 - y1); 493 | 494 | float error = dx / 2; 495 | const int ystep = (y1 < y2) ? 1 : -1; 496 | int y = y1; 497 | 498 | for (int x = x1; x < x2; x++) { 499 | if (steep) { 500 | this->draw_dot(y, x); 501 | } else { 502 | this->draw_dot(x, y); 503 | } 504 | 505 | error -= dy; 506 | if (error < 0) { 507 | y += ystep; 508 | error += dx; 509 | } 510 | } 511 | } 512 | 513 | } // namespace M6502 514 | -------------------------------------------------------------------------------- /src/cpu.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the MOS 6502 Emulator 3 | * (https://github.com/KCreate/mos6502) 4 | * 5 | * MIT License 6 | * 7 | * Copyright (c) 2017 - 2018 Leonard Schütz 8 | * 9 | * Permission is hereby granted, free of charge, to any person obtaining a copy 10 | * of this software and associated documentation files (the "Software"), to deal 11 | * in the Software without restriction, including without limitation the rights 12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | * copies of the Software, and to permit persons to whom the Software is 14 | * furnished to do so, subject to the following conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be included in 17 | * all copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 25 | * SOFTWARE. 26 | */ 27 | 28 | #include 29 | 30 | #include "cpu.h" 31 | 32 | #define DEFINE_OPCODE(HEXCODE, OPNAME, ADDRMODE) \ 33 | instruction.addr = &CPU::addr_##ADDRMODE; \ 34 | instruction.code = &CPU::op_##OPNAME; \ 35 | this->dispatch_table[HEXCODE] = instruction 36 | 37 | namespace M6502 { 38 | 39 | CPU::CPU(Bus* b) : bus(b) { 40 | bus->attach_cpu(this); 41 | 42 | Instruction instruction; 43 | 44 | // Prefill dispatch table with illegal opcode handlers 45 | instruction.addr = &CPU::addr_implied; 46 | instruction.code = &CPU::op_illegal; 47 | for (int i = 0; i < 256; i++) { 48 | this->dispatch_table[i] = instruction; 49 | } 50 | 51 | // Initialize internal status fields 52 | this->illegal_opcode = false; 53 | this->shutdown = false; 54 | this->int_irq = false; 55 | this->int_nmi = false; 56 | this->int_res = false; 57 | 58 | // Fill all valid opcodes 59 | // 60 | // Table taken from https://nesdev.com/6502.txt 61 | 62 | // 0x00 - 0x1F 63 | DEFINE_OPCODE(0x00, brk, implied); 64 | DEFINE_OPCODE(0x01, ora, pre_indexed_indirect); 65 | DEFINE_OPCODE(0x02, wai, implied); // Opcode from the 65C02 66 | DEFINE_OPCODE(0x05, ora, absolute_zero); 67 | DEFINE_OPCODE(0x06, asl, absolute_zero); 68 | DEFINE_OPCODE(0x08, php, implied); 69 | DEFINE_OPCODE(0x09, ora, immediate); 70 | DEFINE_OPCODE(0x0A, asl, accumulator); 71 | DEFINE_OPCODE(0x0D, ora, absolute); 72 | DEFINE_OPCODE(0x0E, asl, absolute); 73 | DEFINE_OPCODE(0x10, bpl, immediate); 74 | DEFINE_OPCODE(0x11, ora, post_indexed_indirect); 75 | DEFINE_OPCODE(0x15, ora, x_indexed_zero); 76 | DEFINE_OPCODE(0x16, asl, x_indexed_zero); 77 | DEFINE_OPCODE(0x18, clc, implied); 78 | DEFINE_OPCODE(0x19, ora, y_indexed); 79 | DEFINE_OPCODE(0x1D, ora, x_indexed); 80 | DEFINE_OPCODE(0x1E, asl, x_indexed); 81 | 82 | // 0x20 - 0x2F 83 | DEFINE_OPCODE(0x20, jsr, absolute); 84 | DEFINE_OPCODE(0x21, and, pre_indexed_indirect); 85 | DEFINE_OPCODE(0x24, bit, absolute_zero); 86 | DEFINE_OPCODE(0x25, and, absolute_zero); 87 | DEFINE_OPCODE(0x26, rol, absolute_zero); 88 | DEFINE_OPCODE(0x28, plp, implied); 89 | DEFINE_OPCODE(0x29, and, immediate); 90 | DEFINE_OPCODE(0x2A, rol, accumulator); 91 | DEFINE_OPCODE(0x2C, bit, absolute); 92 | DEFINE_OPCODE(0x2D, and, absolute); 93 | DEFINE_OPCODE(0x2E, rol, absolute); 94 | DEFINE_OPCODE(0x30, bmi, immediate); 95 | DEFINE_OPCODE(0x31, and, post_indexed_indirect); 96 | DEFINE_OPCODE(0x35, and, x_indexed_zero); 97 | DEFINE_OPCODE(0x36, rol, x_indexed_zero); 98 | DEFINE_OPCODE(0x38, sec, implied); 99 | DEFINE_OPCODE(0x39, and, y_indexed); 100 | DEFINE_OPCODE(0x3D, and, x_indexed); 101 | DEFINE_OPCODE(0x3E, rol, x_indexed); 102 | 103 | // 0x40 - 5F 104 | DEFINE_OPCODE(0x40, rti, implied); 105 | DEFINE_OPCODE(0x41, eor, pre_indexed_indirect); 106 | DEFINE_OPCODE(0x45, eor, absolute_zero); 107 | DEFINE_OPCODE(0x46, lsr, absolute_zero); 108 | DEFINE_OPCODE(0x48, pha, implied); 109 | DEFINE_OPCODE(0x49, eor, immediate); 110 | DEFINE_OPCODE(0x4A, lsr, accumulator); 111 | DEFINE_OPCODE(0x4C, jmp, absolute); 112 | DEFINE_OPCODE(0x4D, eor, absolute); 113 | DEFINE_OPCODE(0x4E, lsr, absolute); 114 | DEFINE_OPCODE(0x50, bvc, immediate); 115 | DEFINE_OPCODE(0x51, eor, post_indexed_indirect); 116 | DEFINE_OPCODE(0x55, eor, x_indexed_zero); 117 | DEFINE_OPCODE(0x56, lsr, x_indexed_zero); 118 | DEFINE_OPCODE(0x58, cli, implied); 119 | DEFINE_OPCODE(0x59, eor, y_indexed); 120 | DEFINE_OPCODE(0x50, eor, x_indexed); 121 | DEFINE_OPCODE(0x5E, lsr, x_indexed); 122 | 123 | // 0x60 - 7F 124 | DEFINE_OPCODE(0x60, rts, implied); 125 | DEFINE_OPCODE(0x61, adc, pre_indexed_indirect); 126 | DEFINE_OPCODE(0x65, adc, absolute_zero); 127 | DEFINE_OPCODE(0x66, ror, absolute_zero); 128 | DEFINE_OPCODE(0x68, pla, implied); 129 | DEFINE_OPCODE(0x69, adc, immediate); 130 | DEFINE_OPCODE(0x6A, ror, accumulator); 131 | DEFINE_OPCODE(0x6C, jmp, indirect); 132 | DEFINE_OPCODE(0x6D, adc, absolute); 133 | DEFINE_OPCODE(0x6E, ror, absolute); 134 | DEFINE_OPCODE(0x70, bvs, immediate); 135 | DEFINE_OPCODE(0x71, adc, post_indexed_indirect); 136 | DEFINE_OPCODE(0x75, adc, x_indexed_zero); 137 | DEFINE_OPCODE(0x76, ror, x_indexed_zero); 138 | DEFINE_OPCODE(0x78, sei, implied); 139 | DEFINE_OPCODE(0x79, adc, y_indexed); 140 | DEFINE_OPCODE(0x7D, adc, x_indexed); 141 | DEFINE_OPCODE(0x7E, ror, x_indexed); 142 | 143 | // 0x80 - 0x9F 144 | DEFINE_OPCODE(0x81, sta, pre_indexed_indirect); 145 | DEFINE_OPCODE(0x84, sty, absolute_zero); 146 | DEFINE_OPCODE(0x85, sta, absolute_zero); 147 | DEFINE_OPCODE(0x86, stx, absolute_zero); 148 | DEFINE_OPCODE(0x88, dey, implied); 149 | DEFINE_OPCODE(0x8A, txa, implied); 150 | DEFINE_OPCODE(0x8C, sty, absolute); 151 | DEFINE_OPCODE(0x8D, sta, absolute); 152 | DEFINE_OPCODE(0x8E, stx, absolute); 153 | DEFINE_OPCODE(0x90, bcc, immediate); 154 | DEFINE_OPCODE(0x91, sta, post_indexed_indirect); 155 | DEFINE_OPCODE(0x94, sty, x_indexed_zero); 156 | DEFINE_OPCODE(0x95, sta, x_indexed_zero); 157 | DEFINE_OPCODE(0x96, stx, y_indexed_zero); 158 | DEFINE_OPCODE(0x98, tya, implied); 159 | DEFINE_OPCODE(0x99, sta, y_indexed); 160 | DEFINE_OPCODE(0x9A, txs, implied); 161 | DEFINE_OPCODE(0x9D, sta, x_indexed); 162 | 163 | // 0xA0 - 0xBF 164 | DEFINE_OPCODE(0xA0, ldy, immediate); 165 | DEFINE_OPCODE(0xA1, lda, pre_indexed_indirect); 166 | DEFINE_OPCODE(0xA2, ldx, immediate); 167 | DEFINE_OPCODE(0xA4, ldy, absolute_zero); 168 | DEFINE_OPCODE(0xA5, lda, absolute_zero); 169 | DEFINE_OPCODE(0xA6, ldx, absolute_zero); 170 | DEFINE_OPCODE(0xA8, tay, implied); 171 | DEFINE_OPCODE(0xA9, lda, immediate); 172 | DEFINE_OPCODE(0xAA, tax, implied); 173 | DEFINE_OPCODE(0xAC, ldy, absolute); 174 | DEFINE_OPCODE(0xAD, lda, absolute); 175 | DEFINE_OPCODE(0xAE, ldx, absolute); 176 | DEFINE_OPCODE(0xB0, bcs, immediate); 177 | DEFINE_OPCODE(0xB1, lda, post_indexed_indirect); 178 | DEFINE_OPCODE(0xB4, ldy, x_indexed_zero); 179 | DEFINE_OPCODE(0xB5, lda, x_indexed_zero); 180 | DEFINE_OPCODE(0xB6, ldx, x_indexed_zero); 181 | DEFINE_OPCODE(0xB8, clv, implied); 182 | DEFINE_OPCODE(0xB9, lda, y_indexed); 183 | DEFINE_OPCODE(0xBA, tsx, implied); 184 | DEFINE_OPCODE(0xBC, ldy, x_indexed); 185 | DEFINE_OPCODE(0xBD, lda, x_indexed); 186 | DEFINE_OPCODE(0xBE, ldx, y_indexed); 187 | 188 | // 0xC0 - 0xDF 189 | DEFINE_OPCODE(0xC0, cpy, immediate); 190 | DEFINE_OPCODE(0xC1, cmp, pre_indexed_indirect); 191 | DEFINE_OPCODE(0xC4, cpy, absolute_zero); 192 | DEFINE_OPCODE(0xC5, cmp, absolute_zero); 193 | DEFINE_OPCODE(0xC6, dec, absolute_zero); 194 | DEFINE_OPCODE(0xC8, iny, implied); 195 | DEFINE_OPCODE(0xC9, cmp, immediate); 196 | DEFINE_OPCODE(0xCA, dex, implied); 197 | DEFINE_OPCODE(0xCC, cpy, absolute); 198 | DEFINE_OPCODE(0xCD, cmp, absolute); 199 | DEFINE_OPCODE(0xCE, dec, absolute); 200 | DEFINE_OPCODE(0xD0, bne, immediate); 201 | DEFINE_OPCODE(0xD1, cmp, post_indexed_indirect); 202 | DEFINE_OPCODE(0xD5, cmp, x_indexed_zero); 203 | DEFINE_OPCODE(0xD6, dec, x_indexed_zero); 204 | DEFINE_OPCODE(0xD8, cld, implied); 205 | DEFINE_OPCODE(0xD9, cmp, y_indexed); 206 | DEFINE_OPCODE(0xDD, cmp, x_indexed); 207 | DEFINE_OPCODE(0xDE, dec, x_indexed); 208 | 209 | // 0xE0 - 0xFF 210 | DEFINE_OPCODE(0xE0, cpx, immediate); 211 | DEFINE_OPCODE(0xE1, sbc, pre_indexed_indirect); 212 | DEFINE_OPCODE(0xE4, cpx, absolute_zero); 213 | DEFINE_OPCODE(0xE5, sbc, absolute_zero); 214 | DEFINE_OPCODE(0xE6, inc, absolute_zero); 215 | DEFINE_OPCODE(0xE8, inx, implied); 216 | DEFINE_OPCODE(0xE9, sbc, immediate); 217 | DEFINE_OPCODE(0xEA, nop, implied); 218 | DEFINE_OPCODE(0xEC, cpx, absolute); 219 | DEFINE_OPCODE(0xED, sbc, absolute); 220 | DEFINE_OPCODE(0xEE, inc, absolute); 221 | DEFINE_OPCODE(0xF0, beq, immediate); 222 | DEFINE_OPCODE(0xF1, sbc, post_indexed_indirect); 223 | DEFINE_OPCODE(0xF5, sbc, x_indexed_zero); 224 | DEFINE_OPCODE(0xF6, inc, x_indexed_zero); 225 | DEFINE_OPCODE(0xF8, sed, implied); 226 | DEFINE_OPCODE(0xF9, sbc, y_indexed); 227 | DEFINE_OPCODE(0xFD, sbc, x_indexed); 228 | DEFINE_OPCODE(0xFE, inc, x_indexed); 229 | 230 | this->handle_res(); 231 | } 232 | 233 | void CPU::start() { 234 | // Run instructions until we encounter an illegal one 235 | // In that case we just return and let the caller 236 | // decide what he wants to do 237 | while (!this->shutdown && !this->illegal_opcode) { 238 | this->cycle(); 239 | } 240 | } 241 | 242 | void CPU::cycle() { 243 | // Check if there was an interrupt 244 | if (!this->I) { 245 | if (this->int_irq) { 246 | this->handle_irq(); 247 | } 248 | } 249 | 250 | if (this->int_nmi) { 251 | this->handle_nmi(); 252 | } 253 | if (this->int_res) { 254 | this->handle_res(); 255 | } 256 | 257 | // For some reason, a sleep of 1 microsecond (?!) improves performance 258 | // considerably, this may have something to do with other threads, mainly 259 | // the drawing and rendering threads, not getting enough cpu time and a sleep 260 | // at this point will yield control to them more often. 261 | // 262 | // Since this emulator isn't really performance focused, this is okay. 263 | std::this_thread::sleep_for(std::chrono::microseconds(1)); 264 | uint8_t opcode = this->bus->read_byte(this->PC++); 265 | Instruction instruction = this->dispatch_table[opcode]; 266 | this->exec_instruction(instruction); 267 | } 268 | 269 | void CPU::handle_irq() { 270 | this->int_irq = false; 271 | this->stack_push_word(this->PC); 272 | this->stack_push_byte(this->STATUS); 273 | this->I = true; 274 | this->PC = this->bus->read_word(kVecIRQ); 275 | 276 | //std::cout << std::hex; 277 | //std::cout << "racket1_pos" << ": " << reinterpret_cast(this->bus->read_byte(0x00)) << std::endl; 278 | //std::cout << "racket2_pos" << ": " << reinterpret_cast(this->bus->read_byte(0x01)) << std::endl; 279 | //std::cout << "score1 " << ": " << reinterpret_cast(this->bus->read_byte(0x02)) << std::endl; 280 | //std::cout << "score2 " << ": " << reinterpret_cast(this->bus->read_byte(0x03)) << std::endl; 281 | //std::cout << "ball_pos_x " << ": " << reinterpret_cast(this->bus->read_byte(0x04)) << std::endl; 282 | //std::cout << "ball_pos_y " << ": " << reinterpret_cast(this->bus->read_byte(0x05)) << std::endl; 283 | //std::cout << "ball_vel_x " << ": " << reinterpret_cast(this->bus->read_byte(0x06)) << std::endl; 284 | //std::cout << "ball_vel_y " << ": " << reinterpret_cast(this->bus->read_byte(0x07)) << std::endl; 285 | //std::cout << std::dec; 286 | } 287 | 288 | void CPU::handle_brk() { 289 | this->stack_push_word(this->PC); 290 | this->stack_push_byte(this->STATUS | kMaskBreak); 291 | this->I = true; 292 | this->PC = this->bus->read_word(kVecBRK); 293 | } 294 | 295 | void CPU::handle_nmi() { 296 | this->int_nmi = false; 297 | this->stack_push_word(this->PC); 298 | this->stack_push_byte(this->STATUS); 299 | this->I = true; 300 | this->PC = this->bus->read_word(kVecNMI); 301 | } 302 | 303 | void CPU::handle_res() { 304 | this->int_res = false; 305 | this->A = 0x00; 306 | this->X = 0x00; 307 | this->Y = 0x00; 308 | this->PC = this->bus->read_word(kVecRES); 309 | this->SP = kStackReset; 310 | this->STATUS = kMaskConstant; 311 | this->illegal_opcode = false; 312 | } 313 | 314 | void CPU::dump_state(std::ostream& out) { 315 | out << std::hex; 316 | 317 | out << "6502 Microcontroller at " << this << '\n'; 318 | out << "Bus: " << this->bus << '\n'; 319 | out << "Accumulator: " << static_cast(this->A) << '\n'; 320 | out << "Index X: " << static_cast(this->X) << '\n'; 321 | out << "Index Y: " << static_cast(this->Y) << '\n'; 322 | out << "Stack Pointer: " << static_cast(this->SP) << '\n'; 323 | out << "Program Counter: " << static_cast(this->PC) << '\n'; 324 | out << "Status Register: " << static_cast(this->STATUS) << '\n' << '\n'; 325 | 326 | out << std::dec; 327 | } 328 | 329 | void CPU::exec_instruction(Instruction instruction) { 330 | uint16_t src = (this->*instruction.addr)(); 331 | (this->*instruction.code)(src); 332 | } 333 | 334 | uint16_t CPU::addr_immediate() { 335 | return this->PC++; 336 | } 337 | 338 | uint16_t CPU::addr_absolute() { 339 | uint16_t addr = this->PC; 340 | this->PC += 2; 341 | return this->bus->read_word(addr); 342 | } 343 | 344 | uint16_t CPU::addr_absolute_zero() { 345 | return this->bus->read_byte(this->PC++); 346 | } 347 | 348 | uint16_t CPU::addr_implied() { 349 | return 0; 350 | } 351 | 352 | uint16_t CPU::addr_accumulator() { 353 | return this->A; 354 | } 355 | 356 | uint16_t CPU::addr_x_indexed() { 357 | uint16_t addr = this->bus->read_word(this->PC); 358 | this->PC += 2; 359 | return addr + this->X; 360 | } 361 | 362 | uint16_t CPU::addr_y_indexed() { 363 | uint16_t addr = this->bus->read_word(this->PC); 364 | this->PC += 2; 365 | return addr + this->Y; 366 | } 367 | 368 | uint16_t CPU::addr_x_indexed_zero() { 369 | uint8_t addr = this->bus->read_byte(this->PC++); 370 | return addr + this->X; 371 | } 372 | 373 | uint16_t CPU::addr_y_indexed_zero() { 374 | uint8_t addr = this->bus->read_byte(this->PC++); 375 | return addr + this->Y; 376 | } 377 | 378 | uint16_t CPU::addr_indirect() { 379 | uint16_t addr = this->bus->read_word(this->PC++); 380 | return this->bus->read_word(addr); 381 | } 382 | 383 | uint16_t CPU::addr_pre_indexed_indirect() { 384 | uint8_t addr = this->bus->read_byte(this->PC++); 385 | 386 | // When adding the 1-byte address and the X-register, wrap around 387 | // addition is used - i.e. the sum is always a zero-page address. 388 | // e.g: FF + 2 = 0001 not 0101 as you might expect 389 | return this->bus->read_word((addr + this->X) & 0xFF); 390 | } 391 | 392 | uint16_t CPU::addr_post_indexed_indirect() { 393 | uint8_t addr = this->bus->read_byte(this->PC++); 394 | return this->bus->read_byte(addr) + this->Y; 395 | } 396 | 397 | void CPU::stack_push_byte(uint8_t value) { 398 | this->bus->write_byte(kStackBase + this->SP, value); 399 | if (this->SP == 0x00) { 400 | this->SP = kStackReset; 401 | } else { 402 | this->SP--; 403 | } 404 | } 405 | 406 | void CPU::stack_push_word(uint16_t value) { 407 | this->stack_push_byte((value >> 8) & 0xFF); 408 | this->stack_push_byte(value & 0xFF); 409 | } 410 | 411 | uint8_t CPU::stack_pop_byte() { 412 | if (this->SP == 0xFF) { 413 | this->SP = 0x00; 414 | } else { 415 | this->SP++; 416 | } 417 | return this->bus->read_byte(kStackBase + this->SP); 418 | } 419 | 420 | uint16_t CPU::stack_pop_word() { 421 | uint8_t lo = this->stack_pop_byte(); 422 | uint8_t hi = this->stack_pop_byte(); 423 | uint16_t result = lo | (hi << 8); 424 | return result; 425 | } 426 | 427 | void CPU::op_illegal(uint16_t) { 428 | this->illegal_opcode = true; 429 | } 430 | 431 | void CPU::op_adc(uint16_t src) { 432 | uint8_t operand = this->bus->read_byte(src); 433 | uint16_t tmp = operand + this->A + (this->C ? 1 : 0); 434 | this->Z = !(tmp & 0xFF); 435 | 436 | if (this->D) { 437 | // TODO: Implement and understand decimal addition 438 | } else { 439 | this->S = tmp & 0x80; 440 | this->V = !((this->A ^ operand) & 0x80) && ((this->A ^ tmp) & 0x80); 441 | this->C = tmp > 0xFF; 442 | } 443 | 444 | this->A = tmp & 0xFF; 445 | } 446 | 447 | void CPU::op_and(uint16_t src) { 448 | uint8_t operand = this->bus->read_byte(src); 449 | uint8_t res = this->A & operand; 450 | this->S = res & 0x80; 451 | this->Z = !res; 452 | this->A = res; 453 | } 454 | 455 | void CPU::op_asl(uint16_t src) { 456 | uint8_t operand = this->bus->read_byte(src); 457 | this->C = operand & 0x80; 458 | operand <<= 1; 459 | this->S = operand & 0x80; 460 | this->Z = !operand; 461 | this->bus->write_byte(src, operand); 462 | } 463 | 464 | void CPU::op_asl_acc(uint16_t) { 465 | uint8_t operand = this->A; 466 | this->C = operand & 0x80; 467 | operand <<= 1; 468 | this->S = operand & 0x80; 469 | this->Z = !operand; 470 | this->A = operand; 471 | } 472 | 473 | void CPU::op_bcc(uint16_t src) { 474 | if (!this->C) { 475 | this->PC += this->bus->read_byte(src); 476 | } 477 | } 478 | 479 | void CPU::op_bcs(uint16_t src) { 480 | if (this->C) { 481 | this->PC += this->bus->read_byte(src); 482 | } 483 | } 484 | 485 | void CPU::op_beq(uint16_t src) { 486 | if (this->Z) { 487 | this->PC += this->bus->read_byte(src); 488 | } 489 | } 490 | 491 | void CPU::op_bit(uint16_t src) { 492 | uint8_t operand = this->bus->read_byte(src); 493 | uint8_t res = operand & this->A; 494 | this->S = res & 0x80; 495 | this->V = res & 0x40; 496 | this->Z = !res; 497 | } 498 | 499 | void CPU::op_bmi(uint16_t src) { 500 | if (this->S) { 501 | this->PC += this->bus->read_byte(src); 502 | } 503 | } 504 | 505 | void CPU::op_bne(uint16_t src) { 506 | if (!this->Z) { 507 | this->PC += this->bus->read_byte(src); 508 | } 509 | } 510 | 511 | void CPU::op_bpl(uint16_t src) { 512 | if (!this->S) { 513 | this->PC += this->bus->read_byte(src); 514 | } 515 | } 516 | 517 | void CPU::op_brk(uint16_t) { 518 | this->PC++; 519 | this->handle_brk(); 520 | } 521 | 522 | void CPU::op_bvc(uint16_t src) { 523 | if (!this->V) { 524 | this->PC += this->bus->read_byte(src); 525 | } 526 | } 527 | 528 | void CPU::op_bvs(uint16_t src) { 529 | if (this->V) { 530 | this->PC += this->bus->read_byte(src); 531 | } 532 | } 533 | 534 | void CPU::op_clc(uint16_t) { 535 | this->C = false; 536 | } 537 | 538 | void CPU::op_cld(uint16_t) { 539 | this->D = false; 540 | } 541 | 542 | void CPU::op_cli(uint16_t) { 543 | this->I = false; 544 | } 545 | 546 | void CPU::op_clv(uint16_t) { 547 | this->V = false; 548 | } 549 | 550 | void CPU::op_cmp(uint16_t src) { 551 | uint16_t result = this->A - this->bus->read_byte(src); 552 | this->C = result < 0x100; 553 | this->S = result & 0x80; 554 | this->Z = !(result & 0xFF); 555 | } 556 | 557 | void CPU::op_cpx(uint16_t src) { 558 | uint16_t result = this->X - this->bus->read_byte(src); 559 | this->C = result < 0x100; 560 | this->S = result & 0x80; 561 | this->Z = !(result & 0xFF); 562 | } 563 | 564 | void CPU::op_cpy(uint16_t src) { 565 | uint16_t result = this->Y - this->bus->read_byte(src); 566 | this->C = result < 0x100; 567 | this->S = result & 0x80; 568 | this->Z = !(result & 0xFF); 569 | } 570 | 571 | void CPU::op_dec(uint16_t src) { 572 | uint8_t operand = this->bus->read_byte(src); 573 | operand = (operand - 1) % 256; 574 | this->S = operand & 0x80; 575 | this->Z = !operand; 576 | this->bus->write_byte(src, operand); 577 | } 578 | 579 | void CPU::op_dex(uint16_t) { 580 | uint8_t operand = this->X; 581 | operand = (operand - 1) % 256; 582 | this->S = operand & 0x80; 583 | this->Z = !operand; 584 | this->X = operand; 585 | } 586 | 587 | void CPU::op_dey(uint16_t) { 588 | uint8_t operand = this->Y; 589 | operand = (operand - 1) % 256; 590 | this->S = operand & 0x80; 591 | this->Z = !operand; 592 | this->Y = operand; 593 | } 594 | 595 | void CPU::op_eor(uint16_t src) { 596 | uint8_t operand = this->bus->read_byte(src); 597 | operand = this->A ^ operand; 598 | this->S = operand & 0x80; 599 | this->Z = !operand; 600 | this->A = operand; 601 | } 602 | 603 | void CPU::op_inc(uint16_t src) { 604 | uint8_t operand = this->bus->read_byte(src); 605 | operand = (operand + 1) % 256; 606 | this->S = operand & 0x80; 607 | this->Z = !operand; 608 | this->bus->write_byte(src, operand); 609 | } 610 | 611 | void CPU::op_inx(uint16_t) { 612 | uint8_t operand = this->X; 613 | operand = (operand + 1) % 256; 614 | this->S = operand & 0x80; 615 | this->Z = !operand; 616 | this->X = operand; 617 | } 618 | 619 | void CPU::op_iny(uint16_t) { 620 | uint8_t operand = this->Y; 621 | operand = (operand + 1) % 256; 622 | this->S = operand & 0x80; 623 | this->Z = !operand; 624 | this->Y = operand; 625 | } 626 | 627 | void CPU::op_jmp(uint16_t src) { 628 | this->PC = src; 629 | } 630 | 631 | void CPU::op_jsr(uint16_t src) { 632 | this->stack_push_word(this->PC - 1); 633 | this->PC = src; 634 | } 635 | 636 | void CPU::op_lda(uint16_t src) { 637 | uint8_t operand = this->bus->read_byte(src); 638 | this->S = operand & 0x80; 639 | this->Z = !operand; 640 | this->A = operand; 641 | } 642 | 643 | void CPU::op_ldx(uint16_t src) { 644 | uint8_t operand = this->bus->read_byte(src); 645 | this->S = operand & 0x80; 646 | this->Z = !operand; 647 | this->X = operand; 648 | } 649 | 650 | void CPU::op_ldy(uint16_t src) { 651 | uint8_t operand = this->bus->read_byte(src); 652 | this->S = operand & 0x80; 653 | this->Z = !operand; 654 | this->Y = operand; 655 | } 656 | 657 | void CPU::op_lsr(uint16_t src) { 658 | uint8_t operand = this->bus->read_byte(src); 659 | this->C = operand & 0x1; 660 | operand >>= 1; 661 | this->S = false; 662 | this->Z = !operand; 663 | this->bus->write_byte(src, operand); 664 | } 665 | 666 | void CPU::op_lsr_acc(uint16_t) { 667 | uint8_t operand = this->A; 668 | this->C = operand & 0x1; 669 | operand >>= 1; 670 | this->S = false; 671 | this->Z = !operand; 672 | this->A = operand; 673 | } 674 | 675 | void CPU::op_nop(uint16_t) { 676 | // do nothing 677 | } 678 | 679 | void CPU::op_ora(uint16_t src) { 680 | uint8_t operand = this->bus->read_byte(src); 681 | operand = this->A | operand; 682 | this->S = operand & 0x80; 683 | this->Z = !operand; 684 | this->A = operand; 685 | } 686 | 687 | void CPU::op_pha(uint16_t) { 688 | this->stack_push_byte(this->A); 689 | } 690 | 691 | void CPU::op_php(uint16_t) { 692 | this->stack_push_byte(this->STATUS | kMaskBreak); 693 | } 694 | 695 | void CPU::op_pla(uint16_t) { 696 | this->A = this->stack_pop_byte(); 697 | this->S = this->A & 0x80; 698 | this->Z = !this->A; 699 | } 700 | 701 | void CPU::op_plp(uint16_t) { 702 | this->STATUS = this->stack_pop_byte(); 703 | this->_ = true; 704 | this->B = false; 705 | } 706 | 707 | void CPU::op_rol(uint16_t src) { 708 | uint16_t operand = this->bus->read_byte(src); 709 | operand <<= 1; 710 | if (this->C) { 711 | operand |= 0x01; 712 | } 713 | this->C = operand > 0xFF; 714 | operand &= 0xFF; 715 | this->S = operand & 0x80; 716 | this->Z = !operand; 717 | this->bus->write_byte(src, operand); 718 | } 719 | 720 | void CPU::op_rol_acc(uint16_t) { 721 | uint16_t operand = this->A; 722 | operand <<= 1; 723 | if (this->C) { 724 | operand |= 0x01; 725 | } 726 | this->C = operand > 0xFF; 727 | operand &= 0xFF; 728 | this->S = operand & 0x80; 729 | this->Z = !operand; 730 | this->A = operand; 731 | } 732 | 733 | void CPU::op_ror(uint16_t src) { 734 | uint16_t operand = this->bus->read_byte(src); 735 | if (this->C) { 736 | operand |= 0x100; 737 | } 738 | this->C = operand & 0x01; 739 | operand >>= 1; 740 | operand &= 0xFF; 741 | this->S = operand & 0x80; 742 | this->Z = !operand; 743 | this->bus->write_byte(src, operand); 744 | } 745 | 746 | void CPU::op_ror_acc(uint16_t) { 747 | uint16_t operand = this->A; 748 | if (this->C) { 749 | operand |= 0x100; 750 | } 751 | this->C = operand & 0x01; 752 | operand >>= 1; 753 | operand &= 0xFF; 754 | this->S = operand & 0x80; 755 | this->Z = !operand; 756 | this->A = operand; 757 | } 758 | 759 | void CPU::op_rti(uint16_t) { 760 | this->STATUS = this->stack_pop_byte() | kMaskConstant; 761 | this->PC = this->stack_pop_word(); 762 | } 763 | 764 | void CPU::op_rts(uint16_t) { 765 | this->PC = this->stack_pop_word() + 1; 766 | } 767 | 768 | void CPU::op_sbc(uint16_t src) { 769 | uint8_t operand = this->bus->read_byte(src); 770 | uint16_t tmp = this->A - operand - (this->C ? 1 : 0); 771 | this->S = tmp & 0x80; 772 | this->Z = !(tmp & 0xFF); 773 | this->V = ((this->A ^ tmp) & 0x80) && ((this->A ^ operand) & 0x80); 774 | 775 | if (this->D) { 776 | // TODO: Implement and understand decimal mode 777 | } 778 | 779 | this->C = tmp < 0x100; 780 | this->A = tmp & 0xFF; 781 | } 782 | 783 | void CPU::op_sec(uint16_t) { 784 | this->C = true; 785 | } 786 | 787 | void CPU::op_sed(uint16_t) { 788 | this->D = true; 789 | } 790 | 791 | void CPU::op_sei(uint16_t) { 792 | this->I = true; 793 | } 794 | 795 | void CPU::op_sta(uint16_t src) { 796 | this->bus->write_byte(src, this->A); 797 | } 798 | 799 | void CPU::op_stx(uint16_t src) { 800 | this->bus->write_byte(src, this->X); 801 | } 802 | 803 | void CPU::op_sty(uint16_t src) { 804 | this->bus->write_byte(src, this->Y); 805 | } 806 | 807 | void CPU::op_tax(uint16_t) { 808 | uint8_t operand = this->A; 809 | this->S = operand & 0x80; 810 | this->Z = !operand; 811 | this->X = operand; 812 | } 813 | 814 | void CPU::op_tay(uint16_t) { 815 | uint8_t operand = this->A; 816 | this->S = operand & 0x80; 817 | this->Z = !operand; 818 | this->Y = operand; 819 | } 820 | 821 | void CPU::op_tsx(uint16_t) { 822 | uint8_t operand = this->SP; 823 | this->S = operand & 0x80; 824 | this->Z = !operand; 825 | this->X = operand; 826 | } 827 | 828 | void CPU::op_txa(uint16_t) { 829 | uint8_t operand = this->X; 830 | this->S = operand & 0x80; 831 | this->Z = !operand; 832 | this->A = operand; 833 | } 834 | 835 | void CPU::op_txs(uint16_t) { 836 | this->SP = this->X; 837 | } 838 | 839 | void CPU::op_tya(uint16_t) { 840 | uint8_t operand = this->Y; 841 | this->S = operand & 0x80; 842 | this->Z = !operand; 843 | this->A = operand; 844 | } 845 | 846 | void CPU::op_wai(uint16_t) { 847 | std::unique_lock lk(this->mutex_int); 848 | this->cv_int.wait(lk, [&] { 849 | return this->int_irq || this->int_nmi || this->int_res; 850 | }); 851 | 852 | if (this->int_nmi) { 853 | this->handle_nmi(); 854 | } 855 | 856 | if (this->int_res) { 857 | this->handle_res(); 858 | } 859 | 860 | // If interrupts are disabled, we just continue with the next instruction 861 | if (!this->I) { 862 | if (this->int_irq) { 863 | this->handle_irq(); 864 | } 865 | } 866 | } 867 | 868 | } // namespace M6502 869 | -------------------------------------------------------------------------------- /include/cpu.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the MOS 6502 Emulator 3 | * (https://github.com/KCreate/mos6502) 4 | * 5 | * MIT License 6 | * 7 | * Copyright (c) 2017 - 2018 Leonard Schütz 8 | * 9 | * Permission is hereby granted, free of charge, to any person obtaining a copy 10 | * of this software and associated documentation files (the "Software"), to deal 11 | * in the Software without restriction, including without limitation the rights 12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | * copies of the Software, and to permit persons to whom the Software is 14 | * furnished to do so, subject to the following conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be included in 17 | * all copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 25 | * SOFTWARE. 26 | */ 27 | 28 | #include 29 | #include 30 | #include 31 | #include // for std::ostream 32 | #include 33 | 34 | #include "bus.h" 35 | 36 | #pragma once 37 | 38 | namespace M6502 { 39 | 40 | // Interrupt vectors 41 | // 42 | // These addresses contain the value that is loaded into the program counter 43 | // when the corresponding interrupt is triggered 44 | // 45 | // maskable external interrupt 46 | static constexpr uint16_t kVecIRQ = 0xFFFE; 47 | 48 | // maskable software interrupt 49 | static constexpr uint16_t kVecBRK = 0xFFFE; 50 | 51 | // non-maskable hardware interrupt 52 | static constexpr uint16_t kVecNMI = 0xFFFA; 53 | 54 | // reset signal, sent at boot or at runtime 55 | static constexpr uint16_t kVecRES = 0xFFFC; 56 | 57 | // Masks for the STATUS register 58 | static constexpr uint8_t kMaskSign = 0x80; 59 | static constexpr uint8_t kMaskOverflow = 0x40; 60 | static constexpr uint8_t kMaskConstant = 0x20; 61 | static constexpr uint8_t kMaskBreak = 0x10; 62 | static constexpr uint8_t kMaskDecimal = 0x08; 63 | static constexpr uint8_t kMaskInterrupt = 0x04; 64 | static constexpr uint8_t kMaskZero = 0x02; 65 | static constexpr uint8_t kMaskCarry = 0x01; 66 | 67 | // Some constants for stack handling 68 | static constexpr uint16_t kStackBase = 0x0100; 69 | static constexpr uint16_t kStackReset = 0xFF; 70 | 71 | // Virtual CPU for the MOS 6502 72 | class CPU { 73 | public: 74 | CPU(Bus* b); 75 | 76 | // Begin executing 77 | void start(); 78 | 79 | // Execute a single instruction 80 | void cycle(); 81 | 82 | // Dump debugging information to a stream 83 | void dump_state(std::ostream& out); 84 | 85 | // Reset interrupt 86 | // 87 | // TODO: Make this thread safe 88 | void interrupt_reset(); 89 | 90 | // private: 91 | // A single CPU instruction 92 | // 93 | // Contains function pointers to the address mode 94 | // and instruction implementation 95 | typedef void (CPU::*CodeExec)(uint16_t); 96 | typedef uint16_t (CPU::*AddrExec)(); 97 | struct Instruction { 98 | AddrExec addr; 99 | CodeExec code; 100 | }; 101 | 102 | // Dipsatch table indexed by instruction opcode 103 | // 104 | // This table is populated at CPU construction 105 | Instruction dispatch_table[256]; 106 | 107 | // Executes a single instruction 108 | void exec_instruction(Instruction instruction); 109 | 110 | // Read and write single bytes from the bus 111 | Bus* bus; 112 | 113 | // Accumulator register 114 | // 115 | // This is the only register that is able to perform math operations 116 | uint8_t A; 117 | 118 | // X Index register 119 | uint8_t X; 120 | 121 | // Y index register 122 | uint8_t Y; 123 | 124 | // Stack pointer 125 | uint8_t SP; 126 | 127 | // Program counter 128 | uint16_t PC; 129 | 130 | // Status register 131 | union { 132 | // Struct bits 133 | // 134 | // S : Sign flag, Set if the result of an operation is negative 135 | // O : Overflow flag, Set when an arithmetic operation overflows 136 | // _ : Unused flag, Should always be set to 1 137 | // B : Break flag, Set when a software interrupt occurs 138 | // D : Decimal flag, Toggles decimal mode (0x00 - 0x99 mapped to 0 - 99) 139 | // I : Interrupt flag, Disables interrupts if set 140 | // Z : Zero flag, Set if the result of an operation is zero 141 | // C : Carry flag, Holds the carry out of the most significant bit in any arithmetic 142 | // operation. In subtraction however, this flag is cleared if a borrow is 143 | // required and set to 1 if no borrow is required. 144 | // Contains the shifted bit on shift operations 145 | struct { 146 | bool C : 1; 147 | bool Z : 1; 148 | bool I : 1; 149 | bool D : 1; 150 | bool B : 1; 151 | bool _ : 1; 152 | bool V : 1; 153 | bool S : 1; 154 | }; 155 | uint8_t STATUS; 156 | }; 157 | 158 | // Stores wether the last instruction was an illegal one 159 | // The CPU should just halt when it encounters an illegal 160 | // instruction 161 | bool illegal_opcode; 162 | 163 | // Set to true if the CPU should shut down. This value is getting 164 | // before the instruction decoding phase. 165 | bool shutdown; 166 | 167 | // Different flag for other threads to set to signal the CPU 168 | // that an interrupt occured. 169 | // 170 | // These variables are used during regular operation of the CPU. 171 | // If a WAI instruction occurs, we use the condition variables 172 | // declared below to handle interrupts. 173 | std::atomic int_irq; 174 | std::atomic int_nmi; 175 | std::atomic int_res; 176 | std::condition_variable cv_int; 177 | std::mutex mutex_int; 178 | 179 | // Methods which handle different interrupts 180 | void handle_irq(); 181 | void handle_brk(); 182 | void handle_nmi(); 183 | void handle_res(); 184 | 185 | // Interact with the stack 186 | void stack_push_byte(uint8_t value); 187 | void stack_push_word(uint16_t value); 188 | uint8_t stack_pop_byte(); 189 | uint16_t stack_pop_word(); 190 | 191 | // One byte immediate value 192 | // 193 | // e.g: LDA #$0x0A 194 | uint16_t addr_immediate(); 195 | 196 | // Two byte address 197 | // 198 | // e.g: LDA $31F6 199 | uint16_t addr_absolute(); 200 | 201 | // One byte address 202 | // 203 | // e.g: LDA $20 204 | uint16_t addr_absolute_zero(); 205 | 206 | // No data, the operand is implied by the instruction 207 | // e.g: TAX 208 | uint16_t addr_implied(); 209 | 210 | // Operand is stored inside accumulator 211 | // 212 | // e.g: ASL 213 | // LSR 214 | // ROL 215 | // ROR 216 | uint16_t addr_accumulator(); 217 | 218 | // Two byte address which is added to the X 219 | // 220 | // e.g: LDA $31F6, X 221 | uint16_t addr_x_indexed(); 222 | 223 | // Two byte address which is added to the Y register 224 | // 225 | // e.g: LDA $31F6, Y 226 | uint16_t addr_y_indexed(); 227 | 228 | // One byte address which is added to the X register 229 | // 230 | // e.g: LDA $20, X 231 | uint16_t addr_x_indexed_zero(); 232 | 233 | // One byte address which is added to the Y register 234 | // 235 | // e.g: STX $20, Y 236 | uint16_t addr_y_indexed_zero(); 237 | 238 | // Two byte address whose bytes are the new location 239 | // Note: this addressing modes only applies to the JMP instruction 240 | // 241 | // e.g: JMP ($215F) 242 | uint16_t addr_indirect(); 243 | 244 | // One byte address which is added to the X register 245 | // The bytes at the calculated address are the operand 246 | // 247 | // e.g: LDA ($3E, X) 248 | uint16_t addr_pre_indexed_indirect(); 249 | 250 | // One byte address whose contents are added to the Y register to form the 251 | // actual address at which the operand is stored 252 | // 253 | // e.g: LDA ($4C), Y 254 | uint16_t addr_post_indexed_indirect(); 255 | 256 | // One byte relative offset 257 | // 258 | // e.g: BEQ $55 259 | uint16_t addr_relative(); 260 | 261 | // CPU instructions 262 | // 263 | // Documentation was largely copied from: https://nesdev.com/6502.txt 264 | // 265 | // The following notation applies to this summary: 266 | // 267 | // A Accumulator ^ Logical Exclusive Or 268 | // X, Y Index Registers fromS Transfer from Stack 269 | // M Memory toS Transfer to Stack 270 | // S Processor Status Register -> Transfer to 271 | // SP Stack Pointer <- Transfer from 272 | // / Change | Logical OR 273 | // _ No Change PC Program Counter 274 | // + Add PCH Program Counter High 275 | // & Logical AND PCL Program Counter Low 276 | // - Subtract OPER OPERAND 277 | // # IMMEDIATE ADDRESSING MODE 278 | // 279 | 280 | // Handles illegal opcodes 281 | void op_illegal(uint16_t src); 282 | 283 | // ADC Add memory to accumulator with carry ADC 284 | // 285 | // Operation: A + M + C -> A, C N Z C I D V 286 | // / / / _ _ / 287 | // 288 | // +----------------+-----------------------+---------+---------+ 289 | // | Addressing Mode| Assembly Language Form| OP CODE |No. Bytes| 290 | // +----------------+-----------------------+---------+---------+ 291 | // | Immediate | ADC #Oper | 69 | 2 | 292 | // | Zero Page | ADC Oper | 65 | 2 | 293 | // | Zero Page,X | ADC Oper,X | 75 | 2 | 294 | // | Absolute | ADC Oper | 6D | 3 | 295 | // | Absolute,X | ADC Oper,X | 70 | 3 | 296 | // | Absolute,Y | ADC Oper,Y | 79 | 3 | 297 | // | (Indirect,X) | ADC (Oper,X) | 61 | 2 | 298 | // | (Indirect),Y | ADC (Oper),Y | 71 | 2 | 299 | // +----------------+-----------------------+---------+---------+ 300 | void op_adc(uint16_t src); 301 | 302 | // AND AND memory with accumulator AND 303 | // 304 | // Operation: A & M -> A N Z C I D V 305 | // / / _ _ _ _ 306 | // 307 | // +----------------+-----------------------+---------+---------+ 308 | // | Addressing Mode| Assembly Language Form| OP CODE |No. Bytes| 309 | // +----------------+-----------------------+---------+---------+ 310 | // | Immediate | AND #Oper | 29 | 2 | 311 | // | Zero Page | AND Oper | 25 | 2 | 312 | // | Zero Page,X | AND Oper,X | 35 | 2 | 313 | // | Absolute | AND Oper | 2D | 3 | 314 | // | Absolute,X | AND Oper,X | 3D | 3 | 315 | // | Absolute,Y | AND Oper,Y | 39 | 3 | 316 | // | (Indirect,X) | AND (Oper,X) | 21 | 2 | 317 | // | (Indirect),Y | AND (Oper),Y | 31 | 2 | 318 | // +----------------+-----------------------+---------+---------+ 319 | void op_and(uint16_t src); 320 | 321 | // ASL Shift left one bit (memory or accumulator) ASL 322 | // 323 | // Operation: C <- M/A <- 0 N Z C I D V 324 | // / / / _ _ _ 325 | // 326 | // +----------------+-----------------------+---------+---------+ 327 | // | Addressing Mode| Assembly Language Form| OP CODE |No. Bytes| 328 | // +----------------+-----------------------+---------+---------+ 329 | // | Accumulator | ASL A | 0A | 1 | 330 | // | Zero Page | ASL Oper | 06 | 2 | 331 | // | Zero Page,X | ASL Oper,X | 16 | 2 | 332 | // | Absolute | ASL Oper | 0E | 3 | 333 | // | Absolute, X | ASL Oper,X | 1E | 3 | 334 | // +----------------+-----------------------+---------+---------+ 335 | void op_asl(uint16_t src); 336 | void op_asl_acc(uint16_t src); 337 | 338 | // BCC Branch on carry clear BCC 339 | // 340 | // Operation: Branch on C = 0 N Z C I D V 341 | // _ _ _ _ _ _ 342 | // 343 | // +----------------+-----------------------+---------+---------+ 344 | // | Addressing Mode| Assembly Language Form| OP CODE |No. Bytes| 345 | // +----------------+-----------------------+---------+---------+ 346 | // | Relative | BCC Oper | 90 | 2 | 347 | // +----------------+-----------------------+---------+---------+ 348 | void op_bcc(uint16_t src); 349 | 350 | // BCS Branch on carry set BCS 351 | // 352 | // Operation: Branch on C = 1 N Z C I D V 353 | // _ _ _ _ _ _ 354 | // 355 | // +----------------+-----------------------+---------+---------+ 356 | // | Addressing Mode| Assembly Language Form| OP CODE |No. Bytes| 357 | // +----------------+-----------------------+---------+---------+ 358 | // | Relative | BCS Oper | B0 | 2 | 359 | // +----------------+-----------------------+---------+---------+ 360 | void op_bcs(uint16_t src); 361 | 362 | // BEQ Branch on result zero BEQ 363 | // 364 | // Operation: Branch on Z = 1 N Z C I D V 365 | // _ _ _ _ _ _ 366 | // 367 | // +----------------+-----------------------+---------+---------+ 368 | // | Addressing Mode| Assembly Language Form| OP CODE |No. Bytes| 369 | // +----------------+-----------------------+---------+---------+ 370 | // | Relative | BEQ Oper | F0 | 2 | 371 | // +----------------+-----------------------+---------+---------+ 372 | void op_beq(uint16_t src); 373 | 374 | // BIT Test bits in memory with accumulator BIT 375 | // 376 | // Operation: A & M, M7 -> N, M6 -> V N Z C I D V 377 | // If the result of A & M is zero then Z = 1, / / _ _ _ / 378 | // otherwise Z = 0 379 | // 380 | // +----------------+-----------------------+---------+---------+ 381 | // | Addressing Mode| Assembly Language Form| OP CODE |No. Bytes| 382 | // +----------------+-----------------------+---------+---------+ 383 | // | Zero Page | BIT Oper | 24 | 2 | 384 | // | Absolute | BIT Oper | 2C | 3 | 385 | // +----------------+-----------------------+---------+---------+ 386 | void op_bit(uint16_t src); 387 | 388 | // BMI Branch on result minus BMI 389 | // 390 | // Operation: Branch on N = 1 N Z C I D V 391 | // _ _ _ _ _ _ 392 | // 393 | // +----------------+-----------------------+---------+---------+ 394 | // | Addressing Mode| Assembly Language Form| OP CODE |No. Bytes| 395 | // +----------------+-----------------------+---------+---------+ 396 | // | Relative | BMI Oper | 30 | 2 | 397 | // +----------------+-----------------------+---------+---------+ 398 | void op_bmi(uint16_t src); 399 | 400 | // BNE Branch on result not zero BNE 401 | // 402 | // Operation: Branch on Z = 0 N Z C I D V 403 | // _ _ _ _ _ _ 404 | // 405 | // +----------------+-----------------------+---------+---------+ 406 | // | Addressing Mode| Assembly Language Form| OP CODE |No. Bytes| 407 | // +----------------+-----------------------+---------+---------+ 408 | // | Relative | BMI Oper | D0 | 2 | 409 | // +----------------+-----------------------+---------+---------+ 410 | void op_bne(uint16_t src); 411 | 412 | // BPL Branch on result plus BPL 413 | // 414 | // Operation: Branch on N = 0 N Z C I D V 415 | // _ _ _ _ _ _ 416 | // 417 | // +----------------+-----------------------+---------+---------+ 418 | // | Addressing Mode| Assembly Language Form| OP CODE |No. Bytes| 419 | // +----------------+-----------------------+---------+---------+ 420 | // | Relative | BPL Oper | 10 | 2 | 421 | // +----------------+-----------------------+---------+---------+ 422 | void op_bpl(uint16_t src); 423 | 424 | // BRK Force break BRK 425 | // 426 | // Operation: Forced interrupt, toS(PC + 2), toS(S) N Z C I D V 427 | // _ _ _ 1 _ _ 428 | // 429 | // +----------------+-----------------------+---------+---------+ 430 | // | Addressing Mode| Assembly Language Form| OP CODE |No. Bytes| 431 | // +----------------+-----------------------+---------+---------+ 432 | // | Implied | BRK | 00 | 1 | 433 | // +----------------+-----------------------+---------+---------+ 434 | void op_brk(uint16_t src); 435 | 436 | // BVC Branch on overflow clear BVC 437 | // 438 | // Operation: Branch on V = 0 N Z C I D V 439 | // _ _ _ _ _ _ 440 | // 441 | // +----------------+-----------------------+---------+---------+ 442 | // | Addressing Mode| Assembly Language Form| OP CODE |No. Bytes| 443 | // +----------------+-----------------------+---------+---------+ 444 | // | Relative | BVC Oper | 50 | 2 | 445 | // +----------------+-----------------------+---------+---------+ 446 | void op_bvc(uint16_t src); 447 | 448 | // BVS Branch on overflow set BVS 449 | // 450 | // Operation: Branch on V = 1 N Z C I D V 451 | // _ _ _ _ _ _ 452 | // 453 | // +----------------+-----------------------+---------+---------+ 454 | // | Addressing Mode| Assembly Language Form| OP CODE |No. Bytes| 455 | // +----------------+-----------------------+---------+---------+ 456 | // | Relative | BVS Oper | 70 | 2 | 457 | // +----------------+-----------------------+---------+---------+ 458 | void op_bvs(uint16_t src); 459 | 460 | // CLC Clear carry flag CLC 461 | // 462 | // Operation: 0 -> C N Z C I D V 463 | // _ _ 0 _ _ _ 464 | // 465 | // +----------------+-----------------------+---------+---------+ 466 | // | Addressing Mode| Assembly Language Form| OP CODE |No. Bytes| 467 | // +----------------+-----------------------+---------+---------+ 468 | // | Implied | CLC | 18 | 1 | 469 | // +----------------+-----------------------+---------+---------+ 470 | void op_clc(uint16_t src); 471 | 472 | // CLD Clear decimal mode CLD 473 | // 474 | // Operation: 0 -> D N Z C I D V 475 | // _ _ _ _ 0 _ 476 | // 477 | // +----------------+-----------------------+---------+---------+ 478 | // | Addressing Mode| Assembly Language Form| OP CODE |No. Bytes| 479 | // +----------------+-----------------------+---------+---------+ 480 | // | Implied | CLD | D8 | 1 | 481 | // +----------------+-----------------------+---------+---------+ 482 | void op_cld(uint16_t src); 483 | 484 | // CLI Clear interrupt disable bit CLI 485 | // 486 | // Operation: 0 -> I N Z C I D V 487 | // _ _ _ 0 _ _ 488 | // 489 | // +----------------+-----------------------+---------+---------+ 490 | // | Addressing Mode| Assembly Language Form| OP CODE |No. Bytes| 491 | // +----------------+-----------------------+---------+---------+ 492 | // | Implied | CLI | 58 | 1 | 493 | // +----------------+-----------------------+---------+---------+ 494 | void op_cli(uint16_t src); 495 | 496 | // CLV Clear overflow flag CLV 497 | // 498 | // Operation: 0 -> V N Z C I D V 499 | // _ _ _ _ _ 0 500 | // 501 | // +----------------+-----------------------+---------+---------+ 502 | // | Addressing Mode| Assembly Language Form| OP CODE |No. Bytes| 503 | // +----------------+-----------------------+---------+---------+ 504 | // | Implied | CLV | B8 | 1 | 505 | // +----------------+-----------------------+---------+---------+ 506 | void op_clv(uint16_t src); 507 | 508 | // CMP Compare memory and accumulator CMP 509 | // 510 | // Operation: A - M N Z C I D V 511 | // / / / _ _ _ 512 | // 513 | // +----------------+-----------------------+---------+---------+ 514 | // | Addressing Mode| Assembly Language Form| OP CODE |No. Bytes| 515 | // +----------------+-----------------------+---------+---------+ 516 | // | Immediate | CMP #Oper | C9 | 2 | 517 | // | Zero Page | CMP Oper | C5 | 2 | 518 | // | Zero Page,X | CMP Oper,X | D5 | 2 | 519 | // | Absolute | CMP Oper | CD | 3 | 520 | // | Absolute,X | CMP Oper,X | DD | 3 | 521 | // | Absolute,Y | CMP Oper,Y | D9 | 3 | 522 | // | (Indirect,X) | CMP (Oper,X) | C1 | 2 | 523 | // | (Indirect),Y | CMP (Oper),Y | D1 | 2 | 524 | // +----------------+-----------------------+---------+---------+ 525 | void op_cmp(uint16_t src); 526 | 527 | // CPX Compare memory and index X CPX 528 | // 529 | // Operation: X - M N Z C I D V 530 | // / / / _ _ _ 531 | // 532 | // +----------------+-----------------------+---------+---------+ 533 | // | Addressing Mode| Assembly Language Form| OP CODE |No. Bytes| 534 | // +----------------+-----------------------+---------+---------+ 535 | // | Immediate | CPX #Oper | E0 | 2 | 536 | // | Zero Page | CPX Oper | E4 | 2 | 537 | // | Absolute | CPX Oper | EC | 3 | 538 | // +----------------+-----------------------+---------+---------+ 539 | void op_cpx(uint16_t src); 540 | 541 | // CPY Compare memory and index Y CPY 542 | // 543 | // Operation: Y - M N Z C I D V 544 | // / / / _ _ _ 545 | // 546 | // +----------------+-----------------------+---------+---------+ 547 | // | Addressing Mode| Assembly Language Form| OP CODE |No. Bytes| 548 | // +----------------+-----------------------+---------+---------+ 549 | // | Immediate | CPY #Oper | C0 | 2 | 550 | // | Zero Page | CPY Oper | C4 | 2 | 551 | // | Absolute | CPY Oper | CC | 3 | 552 | // +----------------+-----------------------+---------+---------+ 553 | void op_cpy(uint16_t src); 554 | 555 | // DEC Decrement memory by one DEC 556 | // 557 | // Operation: M - 1 -> M N Z C I D V 558 | // / / _ _ _ _ 559 | // 560 | // +----------------+-----------------------+---------+---------+ 561 | // | Addressing Mode| Assembly Language Form| OP CODE |No. Bytes| 562 | // +----------------+-----------------------+---------+---------+ 563 | // | Zero Page | DEC Oper | C6 | 2 | 564 | // | Zero Page,X | DEC Oper,X | D6 | 2 | 565 | // | Absolute | DEC Oper | CE | 3 | 566 | // | Absolute,X | DEC Oper,X | DE | 3 | 567 | // +----------------+-----------------------+---------+---------+ 568 | void op_dec(uint16_t src); 569 | 570 | // DEX Decrement index X by one DEX 571 | // 572 | // Operation: X - 1 -> X N Z C I D V 573 | // / / _ _ _ _ 574 | // 575 | // +----------------+-----------------------+---------+---------+ 576 | // | Addressing Mode| Assembly Language Form| OP CODE |No. Bytes| 577 | // +----------------+-----------------------+---------+---------+ 578 | // | Implied | DEX | CA | 1 | 579 | // +----------------+-----------------------+---------+---------+ 580 | void op_dex(uint16_t src); 581 | 582 | // DEY Decrement index Y by one DEY 583 | // 584 | // Operation: Y - 1 -> Y N Z C I D V 585 | // / / _ _ _ _ 586 | // 587 | // +----------------+-----------------------+---------+---------+ 588 | // | Addressing Mode| Assembly Language Form| OP CODE |No. Bytes| 589 | // +----------------+-----------------------+---------+---------+ 590 | // | Implied | DEY | 88 | 1 | 591 | // +----------------+-----------------------+---------+---------+ 592 | void op_dey(uint16_t src); 593 | 594 | // EOR XOR memory with accumulator XOR 595 | // 596 | // Operation: A ^ M -> A N Z C I D V 597 | // / / _ _ _ _ 598 | // 599 | // +----------------+-----------------------+---------+---------+ 600 | // | Addressing Mode| Assembly Language Form| OP CODE |No. Bytes| 601 | // +----------------+-----------------------+---------+---------+ 602 | // | Immediate | EOR #Oper | 49 | 2 | 603 | // | Zero Page | EOR Oper | 45 | 2 | 604 | // | Zero Page,X | EOR Oper,X | 55 | 2 | 605 | // | Absolute | EOR Oper | 40 | 3 | 606 | // | Absolute,X | EOR Oper,X | 50 | 3 | 607 | // | Absolute,Y | EOR Oper,Y | 59 | 3 | 608 | // | (Indirect,X) | EOR (Oper,X) | 41 | 2 | 609 | // | (Indirect),Y | EOR (Oper),Y | 51 | 2 | 610 | // +----------------+-----------------------+---------+---------+ 611 | void op_eor(uint16_t src); 612 | 613 | // INC Increment memory by one INC 614 | // 615 | // Operation: M + 1 -> M N Z C I D V 616 | // / / _ _ _ _ 617 | // 618 | // +----------------+-----------------------+---------+---------+ 619 | // | Addressing Mode| Assembly Language Form| OP CODE |No. Bytes| 620 | // +----------------+-----------------------+---------+---------+ 621 | // | Zero Page | INC Oper | E6 | 2 | 622 | // | Zero Page,X | INC Oper,X | F6 | 2 | 623 | // | Absolute | INC Oper | EE | 3 | 624 | // | Absolute,X | INC Oper,X | FE | 3 | 625 | // +----------------+-----------------------+---------+---------+ 626 | void op_inc(uint16_t src); 627 | 628 | // INX Increment index X by one INX 629 | // 630 | // Operation: X + 1 -> X N Z C I D V 631 | // / / _ _ _ _ 632 | // 633 | // +----------------+-----------------------+---------+---------+ 634 | // | Addressing Mode| Assembly Language Form| OP CODE |No. Bytes| 635 | // +----------------+-----------------------+---------+---------+ 636 | // | Implied | INX | E8 | 1 | 637 | // +----------------+-----------------------+---------+---------+ 638 | void op_inx(uint16_t src); 639 | 640 | // INY Increment index Y by one INY 641 | // 642 | // Operation: Y + 1 -> Y N Z C I D V 643 | // / / _ _ _ _ 644 | // 645 | // +----------------+-----------------------+---------+---------+ 646 | // | Addressing Mode| Assembly Language Form| OP CODE |No. Bytes| 647 | // +----------------+-----------------------+---------+---------+ 648 | // | Implied | INY | C8 | 1 | 649 | // +----------------+-----------------------+---------+---------+ 650 | void op_iny(uint16_t src); 651 | 652 | // JMP Jump to new location JMP 653 | // 654 | // Operation: (PC + 1) -> PCL N Z C I D V 655 | // (PC + 2) -> PCH _ _ _ _ _ _ 656 | // 657 | // +----------------+-----------------------+---------+---------+ 658 | // | Addressing Mode| Assembly Language Form| OP CODE |No. Bytes| 659 | // +----------------+-----------------------+---------+---------+ 660 | // | Absolute | JMP Oper | 4C | 3 | 661 | // | Indirect | JMP (Oper) | 6C | 3 | 662 | // +----------------+-----------------------+---------+---------+ 663 | void op_jmp(uint16_t src); 664 | 665 | // JSR Jump to new location saving return address JSR 666 | // 667 | // Operation: toS(PC + 2), (PC + 1) -> PCL N Z C I D V 668 | // (PC + 2) -> PCH _ _ _ _ _ _ 669 | // 670 | // +----------------+-----------------------+---------+---------+ 671 | // | Addressing Mode| Assembly Language Form| OP CODE |No. Bytes| 672 | // +----------------+-----------------------+---------+---------+ 673 | // | Absolute | JSR Oper | 20 | 3 | 674 | // +----------------+-----------------------+---------+---------+ 675 | void op_jsr(uint16_t src); 676 | 677 | // LDA Load memory to accumulator LDA 678 | // 679 | // Operation: M -> A N Z C I D V 680 | // / / _ _ _ _ 681 | // 682 | // +----------------+-----------------------+---------+---------+ 683 | // | Addressing Mode| Assembly Language Form| OP CODE |No. Bytes| 684 | // +----------------+-----------------------+---------+---------+ 685 | // | Immediate | LDA #Oper | A9 | 2 | 686 | // | Zero Page | LDA Oper | A5 | 2 | 687 | // | Zero Page,X | LDA Oper,X | B5 | 2 | 688 | // | Absolute | LDA Oper | AD | 3 | 689 | // | Absolute,X | LDA Oper,X | BD | 3 | 690 | // | Absolute,Y | LDA Oper,Y | B9 | 3 | 691 | // | (Indirect,X) | LDA (Oper,X) | A1 | 2 | 692 | // | (Indirect),Y | LDA (Oper),Y | B1 | 2 | 693 | // +----------------+-----------------------+---------+---------+ 694 | void op_lda(uint16_t src); 695 | 696 | // LDX Load memory to index X LDX 697 | // 698 | // Operation: M -> X N Z C I D V 699 | // / / _ _ _ _ 700 | // 701 | // +----------------+-----------------------+---------+---------+ 702 | // | Addressing Mode| Assembly Language Form| OP CODE |No. Bytes| 703 | // +----------------+-----------------------+---------+---------+ 704 | // | Immediate | LDX #Oper | A2 | 2 | 705 | // | Zero Page | LDX Oper | A6 | 2 | 706 | // | Zero Page,Y | LDX Oper,Y | B6 | 2 | 707 | // | Absolute | LDX Oper | AE | 3 | 708 | // | Absolute,Y | LDX Oper,Y | BE | 3 | 709 | // +----------------+-----------------------+---------+---------+ 710 | void op_ldx(uint16_t src); 711 | 712 | // LDY Load memory to index Y LDY 713 | // 714 | // Operation: M -> Y N Z C I D V 715 | // / / _ _ _ _ 716 | // 717 | // +----------------+-----------------------+---------+---------+ 718 | // | Addressing Mode| Assembly Language Form| OP CODE |No. Bytes| 719 | // +----------------+-----------------------+---------+---------+ 720 | // | Immediate | LDY #Oper | A0 | 2 | 721 | // | Zero Page | LDY Oper | A4 | 2 | 722 | // | Zero Page,X | LDY Oper,X | B4 | 2 | 723 | // | Absolute | LDY Oper | AC | 3 | 724 | // | Absolute,X | LDY Oper,X | BC | 3 | 725 | // +----------------+-----------------------+---------+---------+ 726 | void op_ldy(uint16_t src); 727 | 728 | // LSR Shift right one bit (memory or accumulator) LSR 729 | // 730 | // Operation: 0 -> M/A -> C N Z C I D V 731 | // 0 / / _ _ _ 732 | // 733 | // +----------------+-----------------------+---------+---------+ 734 | // | Addressing Mode| Assembly Language Form| OP CODE |No. Bytes| 735 | // +----------------+-----------------------+---------+---------+ 736 | // | Accumulator | LSR A | 4A | 1 | 737 | // | Zero Page | LSR Oper | 46 | 2 | 738 | // | Zero Page,X | LSR Oper,X | 56 | 2 | 739 | // | Absolute | LSR Oper | 4E | 3 | 740 | // | Absolute,X | LSR Oper,X | 5E | 3 | 741 | // +----------------+-----------------------+---------+---------+ 742 | void op_lsr(uint16_t src); 743 | void op_lsr_acc(uint16_t src); 744 | 745 | // NOP No operation NOP 746 | // 747 | // Operation: No operation N Z C I D V 748 | // _ _ _ _ _ _ 749 | // 750 | // +----------------+-----------------------+---------+---------+ 751 | // | Addressing Mode| Assembly Language Form| OP CODE |No. Bytes| 752 | // +----------------+-----------------------+---------+---------+ 753 | // | Implied | NOP | EA | 1 | 754 | // +----------------+-----------------------+---------+---------+ 755 | void op_nop(uint16_t src); 756 | 757 | // ORA OR memory with accumulator ORA 758 | // 759 | // Operation: A | M -> A N Z C I D V 760 | // / / _ _ _ _ 761 | // 762 | // +----------------+-----------------------+---------+---------+ 763 | // | Addressing Mode| Assembly Language Form| OP CODE |No. Bytes| 764 | // +----------------+-----------------------+---------+---------+ 765 | // | Immediate | ORA #Oper | 09 | 2 | 766 | // | Zero Page | ORA Oper | 05 | 2 | 767 | // | Zero Page,X | ORA Oper,X | 15 | 2 | 768 | // | Absolute | ORA Oper | 0D | 3 | 769 | // | Absolute,X | ORA Oper,X | 1D | 3 | 770 | // | Absolute,Y | ORA Oper,Y | 19 | 3 | 771 | // | (Indirect,X) | ORA (Oper,X) | 01 | 2 | 772 | // | (Indirect),Y | ORA (Oper),Y | 11 | 2 | 773 | // +----------------+-----------------------+---------+---------+ 774 | void op_ora(uint16_t src); 775 | 776 | // PHA Push accumulator on stack PHA 777 | // 778 | // Operation: toS(A) N Z C I D V 779 | // _ _ _ _ _ _ 780 | // 781 | // +----------------+-----------------------+---------+---------+ 782 | // | Addressing Mode| Assembly Language Form| OP CODE |No. Bytes| 783 | // +----------------+-----------------------+---------+---------+ 784 | // | Implied | PHA | 48 | 1 | 785 | // +----------------+-----------------------+---------+---------+ 786 | void op_pha(uint16_t src); 787 | 788 | // PHP Push processor status on stack PHP 789 | // 790 | // Operation: toS(S) N Z C I D V 791 | // _ _ _ _ _ _ 792 | // 793 | // +----------------+-----------------------+---------+---------+ 794 | // | Addressing Mode| Assembly Language Form| OP CODE |No. Bytes| 795 | // +----------------+-----------------------+---------+---------+ 796 | // | Implied | PHP | 08 | 1 | 797 | // +----------------+-----------------------+---------+---------+ 798 | void op_php(uint16_t src); 799 | 800 | // PLA Pull accumulator from stack PLA 801 | // 802 | // Operation: fromS -> A N Z C I D V 803 | // _ _ _ _ _ _ 804 | // 805 | // +----------------+-----------------------+---------+---------+ 806 | // | Addressing Mode| Assembly Language Form| OP CODE |No. Bytes| 807 | // +----------------+-----------------------+---------+---------+ 808 | // | Implied | PLA | 68 | 1 | 809 | // +----------------+-----------------------+---------+---------+ 810 | void op_pla(uint16_t src); 811 | 812 | // PLP Pull processor status from stack PLP 813 | // 814 | // Operation: fromS -> S N Z C I D V 815 | // From stack 816 | // 817 | // +----------------+-----------------------+---------+---------+ 818 | // | Addressing Mode| Assembly Language Form| OP CODE |No. Bytes| 819 | // +----------------+-----------------------+---------+---------+ 820 | // | Implied | PLP | 28 | 1 | 821 | // +----------------+-----------------------+---------+---------+ 822 | void op_plp(uint16_t src); 823 | 824 | // ROL Rotate one bit left (memory or accumulator) ROL 825 | // 826 | // +------------------------------+ 827 | // | M or A | 828 | // | +-+-+-+-+-+-+-+-+ +-+ | 829 | // Operation: +-< |7|6|5|4|3|2|1|0| <- |C| <-+ N Z C I D V 830 | // +-+-+-+-+-+-+-+-+ +-+ / / / _ _ _ 831 | // 832 | // +----------------+-----------------------+---------+---------+ 833 | // | Addressing Mode| Assembly Language Form| OP CODE |No. Bytes| 834 | // +----------------+-----------------------+---------+---------+ 835 | // | Accumulator | ROL A | 2A | 1 | 836 | // | Zero Page | ROL Oper | 26 | 2 | 837 | // | Zero Page,X | ROL Oper,X | 36 | 2 | 838 | // | Absolute | ROL Oper | 2E | 3 | 839 | // | Absolute,X | ROL Oper,X | 3E | 3 | 840 | // +----------------+-----------------------+---------+---------+ 841 | void op_rol(uint16_t src); 842 | void op_rol_acc(uint16_t src); 843 | 844 | // ROR Rotate one bit right (memory or accumulator) ROR 845 | // 846 | // +------------------------------+ 847 | // | | 848 | // | +-+ +-+-+-+-+-+-+-+-+ | 849 | // Operation: +-> |C| -> |7|6|5|4|3|2|1|0| >-+ N Z C I D V 850 | // +-+ +-+-+-+-+-+-+-+-+ / / / _ _ _ 851 | // 852 | // +----------------+-----------------------+---------+---------+ 853 | // | Addressing Mode| Assembly Language Form| OP CODE |No. Bytes| 854 | // +----------------+-----------------------+---------+---------+ 855 | // | Accumulator | ROR A | 6A | 1 | 856 | // | Zero Page | ROR Oper | 66 | 2 | 857 | // | Zero Page,X | ROR Oper,X | 76 | 2 | 858 | // | Absolute | ROR Oper | 6E | 3 | 859 | // | Absolute,X | ROR Oper,X | 7E | 3 | 860 | // +----------------+-----------------------+---------+---------+ 861 | void op_ror(uint16_t src); 862 | void op_ror_acc(uint16_t src); 863 | 864 | // RTI Return from interrupt RTI 865 | // 866 | // Operation: fromS(S), fromS(PC) N Z C I D V 867 | // From stack 868 | // 869 | // +----------------+-----------------------+---------+---------+ 870 | // | Addressing Mode| Assembly Language Form| OP CODE |No. Bytes| 871 | // +----------------+-----------------------+---------+---------+ 872 | // | Implied | RTI | 40 | 1 | 873 | // +----------------+-----------------------+---------+---------+ 874 | void op_rti(uint16_t src); 875 | 876 | // RTS Return from subroutine RTS 877 | // 878 | // Operation: fromS(PC), PC + 1 -> PC N Z C I D V 879 | // _ _ _ _ _ _ 880 | // 881 | // +----------------+-----------------------+---------+---------+ 882 | // | Addressing Mode| Assembly Language Form| OP CODE |No. Bytes| 883 | // +----------------+-----------------------+---------+---------+ 884 | // | Implied | RTS | 60 | 1 | 885 | // +----------------+-----------------------+---------+---------+ 886 | void op_rts(uint16_t src); 887 | 888 | // SBC Subtract memory from accumulator with borrow SBC 889 | // 890 | // Operation: A - M - C - > A N Z C I D V 891 | // / / / _ _ / 892 | // 893 | // +----------------+-----------------------+---------+---------+ 894 | // | Addressing Mode| Assembly Language Form| OP CODE |No. Bytes| 895 | // +----------------+-----------------------+---------+---------+ 896 | // | Immediate | SBC #Oper | E9 | 2 | 897 | // | Zero Page | SBC Oper | E5 | 2 | 898 | // | Zero Page,X | SBC Oper,X | F5 | 2 | 899 | // | Absolute | SBC Oper | ED | 3 | 900 | // | Absolute,X | SBC Oper,X | FD | 3 | 901 | // | Absolute,Y | SBC Oper,Y | F9 | 3 | 902 | // | (Indirect,X) | SBC (Oper,X) | E1 | 2 | 903 | // | (Indirect),Y | SBC (Oper),Y | F1 | 2 | 904 | // +----------------+-----------------------+---------+---------+ 905 | void op_sbc(uint16_t src); 906 | 907 | // SEC Set carry flag SEC 908 | // 909 | // Operation: 1 -> C N Z C I D V 910 | // _ _ 1 _ _ _ 911 | // 912 | // +----------------+-----------------------+---------+---------+ 913 | // | Addressing Mode| Assembly Language Form| OP CODE |No. Bytes| 914 | // +----------------+-----------------------+---------+---------+ 915 | // | Implied | SEC | 38 | 1 | 916 | // +----------------+-----------------------+---------+---------+ 917 | void op_sec(uint16_t src); 918 | 919 | // SED Set decimal mode SED 920 | // 921 | // Operation: 1 -> D N Z C I D V 922 | // _ _ _ _ 1 _ 923 | // 924 | // +----------------+-----------------------+---------+---------+ 925 | // | Addressing Mode| Assembly Language Form| OP CODE |No. Bytes| 926 | // +----------------+-----------------------+---------+---------+ 927 | // | Implied | SED | F8 | 1 | 928 | // +----------------+-----------------------+---------+---------+ 929 | void op_sed(uint16_t src); 930 | 931 | // SEI Set interrupt disable status SEI 932 | // 933 | // Operation: 1 -> I N Z C I D V 934 | // _ _ _ 1 _ _ 935 | // 936 | // +----------------+-----------------------+---------+---------+ 937 | // | Addressing Mode| Assembly Language Form| OP CODE |No. Bytes| 938 | // +----------------+-----------------------+---------+---------+ 939 | // | Implied | SEI | 78 | 1 | 940 | // +----------------+-----------------------+---------+---------+ 941 | void op_sei(uint16_t src); 942 | 943 | // STA Store accumulator in memory STA 944 | // 945 | // Operation: A -> M N Z C I D V 946 | // _ _ _ _ _ _ 947 | // 948 | // +----------------+-----------------------+---------+---------+ 949 | // | Addressing Mode| Assembly Language Form| OP CODE |No. Bytes| 950 | // +----------------+-----------------------+---------+---------+ 951 | // | Zero Page | STA Oper | 85 | 2 | 952 | // | Zero Page,X | STA Oper,X | 95 | 2 | 953 | // | Absolute | STA Oper | 8D | 3 | 954 | // | Absolute,X | STA Oper,X | 9D | 3 | 955 | // | Absolute,Y | STA Oper, Y | 99 | 3 | 956 | // | (Indirect,X) | STA (Oper,X) | 81 | 2 | 957 | // | (Indirect),Y | STA (Oper),Y | 91 | 2 | 958 | // +----------------+-----------------------+---------+---------+ 959 | void op_sta(uint16_t src); 960 | 961 | // STX Store index X in memory STX 962 | // 963 | // Operation: X -> M N Z C I D V 964 | // _ _ _ _ _ _ 965 | // 966 | // +----------------+-----------------------+---------+---------+ 967 | // | Addressing Mode| Assembly Language Form| OP CODE |No. Bytes| 968 | // +----------------+-----------------------+---------+---------+ 969 | // | Zero Page | STX Oper | 86 | 2 | 970 | // | Zero Page,Y | STX Oper,Y | 96 | 2 | 971 | // | Absolute | STX Oper | 8E | 3 | 972 | // +----------------+-----------------------+---------+---------+ 973 | void op_stx(uint16_t src); 974 | 975 | // STY Store index Y in memory STY 976 | // 977 | // Operation: Y -> M N Z C I D V 978 | // _ _ _ _ _ _ 979 | // 980 | // +----------------+-----------------------+---------+---------+ 981 | // | Addressing Mode| Assembly Language Form| OP CODE |No. Bytes| 982 | // +----------------+-----------------------+---------+---------+ 983 | // | Zero Page | STY Oper | 84 | 2 | 984 | // | Zero Page,X | STY Oper,X | 94 | 2 | 985 | // | Absolute | STY Oper | 8C | 3 | 986 | // +----------------+-----------------------+---------+---------+ 987 | void op_sty(uint16_t src); 988 | 989 | // TAX Transfer accumulator to index X TAX 990 | // 991 | // Operation: A -> X N Z C I D V 992 | // / / _ _ _ _ 993 | // 994 | // +----------------+-----------------------+---------+---------+ 995 | // | Addressing Mode| Assembly Language Form| OP CODE |No. Bytes| 996 | // +----------------+-----------------------+---------+---------+ 997 | // | Implied | TAX | AA | 1 | 998 | // +----------------+-----------------------+---------+---------+ 999 | void op_tax(uint16_t src); 1000 | 1001 | // TAY Transfer accumulator to index Y TAY 1002 | // 1003 | // Operation: A -> Y N Z C I D V 1004 | // / / _ _ _ _ 1005 | // 1006 | // +----------------+-----------------------+---------+---------+ 1007 | // | Addressing Mode| Assembly Language Form| OP CODE |No. Bytes| 1008 | // +----------------+-----------------------+---------+---------+ 1009 | // | Implied | TAY | A8 | 1 | 1010 | // +----------------+-----------------------+---------+---------+ 1011 | void op_tay(uint16_t src); 1012 | 1013 | // TSX Transfer stack pointer to index X TSX 1014 | // 1015 | // Operation: SP -> X N Z C I D V 1016 | // / / _ _ _ _ 1017 | // 1018 | // +----------------+-----------------------+---------+---------+ 1019 | // | Addressing Mode| Assembly Language Form| OP CODE |No. Bytes| 1020 | // +----------------+-----------------------+---------+---------+ 1021 | // | Implied | TSX | BA | 1 | 1022 | // +----------------+-----------------------+---------+---------+ 1023 | void op_tsx(uint16_t src); 1024 | 1025 | // TXA Transfer index X to accumulator TXA 1026 | // 1027 | // Operation: X -> A N Z C I D V 1028 | // / / _ _ _ _ 1029 | // 1030 | // +----------------+-----------------------+---------+---------+ 1031 | // | Addressing Mode| Assembly Language Form| OP CODE |No. Bytes| 1032 | // +----------------+-----------------------+---------+---------+ 1033 | // | Implied | TXA | 8A | 1 | 1034 | // +----------------+-----------------------+---------+---------+ 1035 | void op_txa(uint16_t src); 1036 | 1037 | // TXS Transfer index X to stack pointer TXS 1038 | // 1039 | // Operation: X -> SP N Z C I D V 1040 | // _ _ _ _ _ _ 1041 | // 1042 | // +----------------+-----------------------+---------+---------+ 1043 | // | Addressing Mode| Assembly Language Form| OP CODE |No. Bytes| 1044 | // +----------------+-----------------------+---------+---------+ 1045 | // | Implied | TXS | 9A | 1 | 1046 | // +----------------+-----------------------+---------+---------+ 1047 | void op_txs(uint16_t src); 1048 | 1049 | // TYA Transfer index Y to accumulator TYA 1050 | // 1051 | // Operation: Y -> A N Z C I D V 1052 | // / / _ _ _ _ 1053 | // 1054 | // +----------------+-----------------------+---------+---------+ 1055 | // | Addressing Mode| Assembly Language Form| OP CODE |No. Bytes| 1056 | // +----------------+-----------------------+---------+---------+ 1057 | // | Implied | TYA | 98 | 1 | 1058 | // +----------------+-----------------------+---------+---------+ 1059 | void op_tya(uint16_t src); 1060 | 1061 | // WAI Wait for an interrupt to happen WAI 1062 | // 1063 | // Operation: PC + 1 -> PC, Wait for interrupt N Z C I D V 1064 | // _ _ _ _ _ _ 1065 | // 1066 | // +----------------+-----------------------+---------+---------+ 1067 | // | Addressing Mode| Assembly Language Form| OP CODE |No. Bytes| 1068 | // +----------------+-----------------------+---------+---------+ 1069 | // | Implied | WAI | 02 | 1 | 1070 | // +----------------+-----------------------+---------+---------+ 1071 | void op_wai(uint16_t src); 1072 | }; 1073 | } // namespace M6502 1074 | --------------------------------------------------------------------------------