Commit: 1cbd58796f8ac1e59c3a1df5c70babb73a836410 Parent: 1c20569460b8d84f2b9023209d7063bdcf10ebc9 Author: Vi Grey Date: 2020-09-15 14:49 UTC Summary: Add TAStm32 program and standalone lua script Makefile | 3 - README.md | 168 +++++++++++++++++++++++++++------------ bin/iss-nes-TAStm32/iss-nes-TAStm32-linux-amd64 | Bin 0 -> 3238048 bytes bin/iss-nes-TAStm32/iss-nes-TAStm32-linux-arm | Bin 0 -> 2742904 bytes bin/iss-nes-TAStm32/iss-nes-TAStm32-linux-arm64 | Bin 0 -> 3015688 bytes bin/iss-nes-TAStm32/udev/rules.d/51-tastm32.rules | 1 + bin/lua/fceux.lua | 91 +++++++++++++++++++++ bin/lua/mesen.lua | 88 +++++++++++++++++++++ bin/{ => nes}/iss-nes.nes | Bin 40976 -> 40976 bytes bin/server-linux-386 | Bin 6285154 -> 0 bytes bin/server-linux-amd64 | Bin 7062786 -> 0 bytes bin/server-linux-arm | Bin 6288789 -> 0 bytes bin/server-linux-arm64 | Bin 6674070 -> 0 bytes bin/server-windows-386.exe | Bin 6043136 -> 0 bytes bin/server-windows-amd64.exe | Bin 6740480 -> 0 bytes iss-nes-TAStm32/iss-nes-TAStm32.go | 341 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ iss-nes-TAStm32/udev/51-tastm32.rules | 1 + lua/fceux.lua | 72 ++++++++++++++--- lua/mesen.lua | 68 +++++++++++++--- server/server.go | 92 ---------------------- src/controller.asm | 141 ++++++++++++++++----------------- src/prg.asm | 2 +- src/ram.asm | 1 + 23 files changed, 826 insertions(+), 243 deletions(-) diff --git a/Makefile b/Makefile index 998eac4..676d257 100644 --- a/Makefile +++ b/Makefile @@ -30,6 +30,3 @@ all: mkdir -p $(CURRENTDIR)bin; \ cd $(CURRENTDIR)src; \ asm $(PKG_NAME).asm ../bin/$(PKG_NAME).nes; \ - -clean: - rm -rf -- $(CURRENTDIR)bin; \ diff --git a/README.md b/README.md index 01c35b0..30b2a3d 100644 --- a/README.md +++ b/README.md @@ -14,99 +14,167 @@ This is an NROM NES ROM that tracks the location of the International Space Stat ------ ## **RUNNING THE NES ROM ON AN EMULATOR** -_**(Source Code Build Instructions are 1 section below)**_ -This NES ROM has only been successfully tested on two NES emulators, FCEUX for Windows and Mesen for Linux (This will likely work for Mesen on Windows as well). **YOU WILL NEED TO RUN AN INCLUDED LUA SCRIPT AND THE ACCOMPANYING SERVER TO MAKE THIS NES ROM WORK PROPERLY ON AN EMULATOR.** +This NES ROM has only been successfully tested on two NES emulators, Mesen and the Windows version of FCEUX for Windows. **YOU WILL NEED TO RUN AN INCLUDED LUA SCRIPT TO MAKE THIS NES ROM WORK PROPERLY ON AN EMULATOR.** ### Running the NES ROM: -In the main directory of this project, you will find a `bin` directory. Inside of this directory is a file named `iss-nes.nes`, which can be opened up in FCEUX on Windows or Mesen on Linux (and possibly Windows as well). To make this NES ROM work for real-time tracking of the International Space Station, you will need to run an included lua script and the accompanying server program. - -### Running the Accompanying Server: -In the main directory of this project, you will find a `bin` directory. Inside of this directory are files named `server-linux-386`, `server-linux-amd64`, `server-linux-arm`, `server-linux-arm64`, `server-windows-386.exe`, and `server-windows-amd64.exe`. Depending on whether you are running Linux or Windows, and what CPU architecture you are using, you will need to run the correct server program. This server program will open up a **localhost** connection to port **56502** that the included Lua scripts are able to connect to in order to get the ISS real-time tracking location. +In the main directory of this project (the directory this README.md file exists in), you will find a `bin` directory. Inside of this directory is a directory named `nes`, which contains a file named `iss-nes.nes`, which can be opened up in Mesen or the Windows version of FCEUX. To make this NES ROM work for real-time tracking of the International Space Station, you will need to run an included lua script. ### Running an Included Lua Script on an Emulator: -In the main directory of this project, you will find a `lua` directory. Inside of that directory is a file called `fceux.lua`, which is used for FCEUX on Windows, and a file called `mesen.lua`, which is used for Mesen. These lua scripts request real-time ISS tracking data (10 bytes of data) from the accompanying server program every frame and displays the information in the NES game while it's running. +In the main directory of this project (the directory this README.md file exists in), you will find a `bin` directory. Inside of this directory is a directory named `lua`, which contains two files named `fceux.lua` and `mesen.lua`. You can use `mesen.lua` in Mesen and `fceux.lua` in the Windows version of FCEUX. These lua scripts request real-time ISS tracking data from `vigrey.com` on port `56502` every minute and displays the information in the NES game while it's running. ------ -## **BUILDING THE SOURCE CODE** +## **BUILDING THE NES ROM SOURCE CODE** ### NES ROM Build Dependencies: - **asm6** (You'll probably have to build asm6 from source. Make sure the asm6 binary is named **asm** and that the binary is executable and accessible in your PATH. The source code can be found at http://3dscapture.com/NES/asm6.zip) - **gmake** (make) -### Accompanying Server Build Dependencies: -- **go** - ### Build NES ROM on Linux: From a terminal, go to the the main directory of this project (the directory this README.md file exists in). You can then build the NES ROM with the following command. ```sh make ``` + The resulting NES ROM will be located at `bin/iss-nes.nes` from the main directory. ### Build NES ROM on Windows: If you are using Windows, in the command prompt (make sure to have asm6 on your system as `asm.exe`), go to the the `src` directory of this project (the `src` directory this README.md file exists in). You can then build the NES ROM with the following command. ``` -asm iss-nes.asm ..\bin\iss-nes.nes +asm.exe iss-nes.asm ..\bin\iss-nes.nes ``` -Replace the `asm` command with the path to your `asm.exe` command. + +Replace the `asm.exe` command with the path to your `asm6` executable. The resulting NES ROM will be located at `bin\iss-nes.nes` from the main directory. -### Build Accompanying Server on Linux: +------ +## **RUNNING THE NES ROM ON ACTUAL NES HARDWARE** _(Requires Linux and a Tool Assisted Speedrun Replay Device)_ + +### Making the NES ROM work on an NES Cartridge: +To run the ISS-NES ROM on actual hardware, you will need to be able to program it into a cartridge. This ROM is compatible with NROM-256 boards and uses Vertical Mirroring. If you cannot program an NROM-256 board with this ROM, you can alternatively use an Everdrive NES flash cartridge by Krikzz or similar device. -If you are using Linux, go to the `bin` directory of this project (the `bin` directory in the directory this README.md file exists in). You can then build the server binary file with the following command. +### Transferring ISS Tracking Data to the NES Console: +A device that is able to press buttons accurately multiple times a frame is required, as frames that get ISS Tracking data will need to be able to send at least 11 bytes of data to the NES console over the Controller 1 port. Usually, when a game asks for what buttons a controller is pressing each frame, the controller will respond back with a single byte that frame. The 11 bytes is for a single frame of data, although you only need to send data on frames where you have updated data. With that said, it's still humanly impossible to send accurate button inputs that quickly, so you will need the help of a Tool Assisted Speedrun replay device (If you are unfamiliar with Tool Assisted Speedruns or Tool Assisted Speedrun replay devices, you can check out TASBot online to see such devices in action, especially at Games Done Quick events). + +A device called the TAStm32 can be used for this purpose, which can be purchased at https://store.tas.bot/owna/index.php/product/tastm32-a-tas-replay-device/. You will also need to create a custom RJ45 Ethernet to NES Controller cable in order for the TAStm32 to connect to the NES console over the controller port. Documentation on how how to create such a cable can be found at https://github.com/Ownasaurus/TAStm32/wiki/Hardware. + +### Controlling the TAStm32 Board With the ISS Tracker NES ROM: +In the main directory of this project (the directory this README.md file exists in), you will find a `bin` directory. Inside of this directory is a directory named `iss-nes-TAStm32`, which contains multiple files that start with `iss-nes-TAStm32-linux-`. The last part of the file names is the architecture of the Linux computer you will be running the program on to control the TAStm32. The available architectures in this directory are **amd64**, which is useful for most laptop and desktop Linux computers, **arm**, which is useful for the Raspberry Pi, and **arm64** for 64 bit arm Linux computers. + +### Usage of iss-nes-TAStm32: +The following command will not include the `-linux-*` part of the program for the sake of simplicity, but do know that the precompiled iss-nes-TAStm32 program may have more to its name than just iss-nes-TAStm32. ``` -go build ../server/server.go +./iss-nes-TAStm32 +Available Commands: start, restart, stop, quit, help + +ENTER COMMAND: ``` -The resulting server binary file will be located at `bin/server` from the main directory. +Upon starting iss-nes-TAStm32, a TCP connection will be made to `vigrey.com` at port `56502` every 60 seconds to get 70 seconds of ISS tracking data. This program will not connect to the TAStm32 board until you type `start` in after running the command. You may need to add the included `udev` rules in the `udev` directory inside of the `iss-nes-TAStm32` directory if you want to connect to the TAStm32 board without running iss-nes-TAStm32 as root. + +------ +## **BUILDING THE ISS-NES SPECIFIC TASTM32 CONTROLLER SOURCE CODE** _(Linux Only)_ -### Build Accompanying Server on Windows: +### iss-nes-TAStm32 Build Dependencies: +- **go** (with cgo) -If you are using Windows, go to the `bin` directory of this project (the `bin` directory in the directory this README.md file exists in). You can then build the server binary file with the following command (Assuming you have the `go` command added to your PATH). +This program depends on mikepb's go-serial package in order to compile. To get the go-serial package, run the following command. ```sh -go build ..\server\server.go +go get github.com/mikepb/go-serial ``` -The resulting server binary file will be located at `bin\server.exe` from the main directory. - -### Cleaning Build Environment (Linux Only): -If you used `make` to build the NES ROM, you can run the following command to clean up the build environment. +Inside of the main directory of this project (the directory this README.md file exists in), you will find a directory named `iss-nes-TAStm32` which contains a file named `iss-nes-TAStm32.go`. You can compile the iss-nes-TAStm32 program with the following command. ```sh -make clean +go build iss-nes-TAStm32.go ``` +The resulting file will be located at `iss-nes-TAStm32/iss-nes-TAStm32` from tha main directory. + ------ +## **ISS TRACKING DATA TO CONTROLLER PROTOCOL** +This game uses the Controller 1 port to retrieve ISS tracking data. If you are using the controller port rather than the provided lua scripts to get ISS tracking data, you MUST send a "sync" pulse byte of $FF (11111111) before sending the first byte of ISS tracking data. It is recommended that you send 2-4 sync pulse bytes before sending ISS tracking data. -## **LICENSE** +All bytes MUST be sent over the D0 line of the controller 1 port to be valid. - Copyright (C) 2020, Vi Grey - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions - are met: - - 1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - 2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS \`\`AS IS'' AND - ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE - FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - SUCH DAMAGE. +The following is the protocol for the 10 data bytes (does not include the sync byte[s]) that are sent to the NES console over controller port 1. + +``` +Bit 76543210 First Byte ($0500) + ||||++++- Day Ones Digit + ||++----- Day Tens Digit + ++------- Hour Tens Digit + +Bit 76543210 Second Byte ($0501) + ||||++++- Year Hundreds Digit + ++++----- Year Thousands Digit + +Bit 76543210 Third Byte ($0502) + ||||++++- Year Ones Digit + ++++----- Year Tens Digit + +Bit 76543210 Fourth Byte ($0503) + ||||++++- Month Offset Minus 1 + ++++----- Hour Ones Digit + +Bit 76543210 Fifth Byte ($0504) + ||||++++- Latitude Ones Digit + |+++----- Latitude Tens Digit + +-------- Longitude Hundreds Digit + +Bit 76543210 Sixth Byte ($0505) + ||||++++- Latitude Hundredths Place + ++++----- Latitude Tenths Place + +Bit 76543210 Seventh Byte ($0506) + ||||++++- Longitude Ones Digit + ++++----- Longitude Tens Digit + +Bit 76543210 Eighth Byte ($0507) + ||||++++- Longitude Hundredths Place + ++++----- Longitude Tenths Place + +Bit 76543210 Ninth Byte ($0508) + ||||++++- Second Ones Digit + |+++----- Second Tens Digit + +-------- Latitude Hemisphere (0=North/1=South) + +Bit 76543210 Tenth Byte ($0509) + ||||++++- Minute Ones Digit + |+++----- Minute Tens Digit + +-------- Longitude Hemisphere (0=East/1=West) +``` + +------ +## **LICENSE** +``` +Copyright (C) 2020, Vi Grey +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS \`\`AS IS'' AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. +``` diff --git a/bin/iss-nes-TAStm32/iss-nes-TAStm32-linux-amd64 b/bin/iss-nes-TAStm32/iss-nes-TAStm32-linux-amd64 new file mode 100755 index 0000000..c18395b Binary files /dev/null and b/bin/iss-nes-TAStm32/iss-nes-TAStm32-linux-amd64 differ diff --git a/bin/iss-nes-TAStm32/iss-nes-TAStm32-linux-arm b/bin/iss-nes-TAStm32/iss-nes-TAStm32-linux-arm new file mode 100755 index 0000000..ca17060 Binary files /dev/null and b/bin/iss-nes-TAStm32/iss-nes-TAStm32-linux-arm differ diff --git a/bin/iss-nes-TAStm32/iss-nes-TAStm32-linux-arm64 b/bin/iss-nes-TAStm32/iss-nes-TAStm32-linux-arm64 new file mode 100755 index 0000000..42e6ffd Binary files /dev/null and b/bin/iss-nes-TAStm32/iss-nes-TAStm32-linux-arm64 differ diff --git a/bin/iss-nes-TAStm32/udev/rules.d/51-tastm32.rules b/bin/iss-nes-TAStm32/udev/rules.d/51-tastm32.rules new file mode 100644 index 0000000..3af7d98 --- /dev/null +++ b/bin/iss-nes-TAStm32/udev/rules.d/51-tastm32.rules @@ -0,0 +1 @@ +SUBSYSTEM=="tty", ATTRS{idVendor}=="0b07", ATTRS{idProduct}=="07a5", ATTRS{serial}=="0000:00:1a.0", MODE="0666" diff --git a/bin/lua/fceux.lua b/bin/lua/fceux.lua new file mode 100644 index 0000000..7d79df6 --- /dev/null +++ b/bin/lua/fceux.lua @@ -0,0 +1,91 @@ +-- Copyright (C) 2020, Vi Grey +-- All rights reserved. +-- +-- Redistribution and use in source and binary forms, with or without +-- modification, are permitted provided that the following conditions +-- are met: +-- +-- 1. Redistributions of source code must retain the above copyright +-- notice, this list of conditions and the following disclaimer. +-- 2. Redistributions in binary form must reproduce the above copyright +-- notice, this list of conditions and the following disclaimer in the +-- documentation and/or other materials provided with the distribution. +-- +-- THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND +-- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +-- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +-- ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE +-- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +-- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +-- OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +-- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +-- LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +-- OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +-- SUCH DAMAGE. + +local socket = require 'socket.core' + +local HOST = "vigrey.com" +local PORT = 56502 +local partial = "" +local second = 0 +local tcpCheckLastTimestamp = 0 + +-- TCP request to HOST:PORT for ISS Data +function getISSData() + local tcp = assert(socket.tcp()) + tcp:settimeout(5) + tcp:connect(HOST, PORT) + local s, status, partial = tcp:receive() + tcp:close() + return partial +end + +-- Write all 0s to the 10 input memory bytes and then wait 5 seconds +function writeBlankFrame() + for i = 10,1,-1 + do + memory.writebyte(1280+i-1, 0) + end + socket.sleep(5) + tcpCheckLastTimestamp = 0 + second = 0 +end + +-- Write minute worth of ISS tracking data to input memory bytes +function writeSecondData(partial) + local timestamp = os.time(os.date("!*t")) + local timestampLast = os.time(os.date("!*t")) + for i = 10,1,-1 + do + memory.writebyte(1280+i-1, string.byte(partial, (second-1)*10+i)) + end + while timestamp == timestampLast do + timestamp = os.time(os.date("!*t")) + socket.sleep(0.1) + end + timestampLast = timestamp +end + +-- Run at beginning of frame +function handleFrame() + memory.writebyte(0, 1) + second = second + 1 + local timestampNow = os.time(os.date("!*t")) + if timestampNow - tcpCheckLastTimestamp > 59 then + tcpCheckLastTimestamp = timestampNow + partial = getISSData() + second = 1 + end + if string.len(partial) > 600 then + writeSecondData(partial) + else + writeBlankFrame() + end +end + +-- Per frame data +while true do + handleFrame() + emu.frameadvance() +end diff --git a/bin/lua/mesen.lua b/bin/lua/mesen.lua new file mode 100644 index 0000000..a3a2736 --- /dev/null +++ b/bin/lua/mesen.lua @@ -0,0 +1,88 @@ +-- Copyright (C) 2020, Vi Grey +-- All rights reserved. +-- +-- Redistribution and use in source and binary forms, with or without +-- modification, are permitted provided that the following conditions +-- are met: +-- +-- 1. Redistributions of source code must retain the above copyright +-- notice, this list of conditions and the following disclaimer. +-- 2. Redistributions in binary form must reproduce the above copyright +-- notice, this list of conditions and the following disclaimer in the +-- documentation and/or other materials provided with the distribution. +-- +-- THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND +-- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +-- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +-- ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE +-- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +-- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +-- OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +-- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +-- LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +-- OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +-- SUCH DAMAGE. + +local socket = require 'socket.core' + +local HOST = "vigrey.com" +local PORT = 56502 +local partial = "" +local second = 0 +local tcpCheckLastTimestamp = 0 + +-- TCP request to HOST:PORT for ISS Data +function getISSData() + local tcp = assert(socket.tcp()) + tcp:settimeout(5) + tcp:connect(HOST, PORT) + local s, status, partial = tcp:receive() + tcp:close() + return partial +end + +-- Write all 0s to the 10 input memory bytes and then wait 5 seconds +function writeBlankFrame() + for i = 10,1,-1 + do + emu.write(1280+i-1, 0, emu.memType.cpu) + end + socket.sleep(5) + tcpCheckLastTimestamp = 0 + second = 0 +end + +-- Write minute worth of ISS tracking data to input memory bytes +function writeSecondData(partial) + local timestamp = os.time(os.date("!*t")) + local timestampLast = os.time(os.date("!*t")) + for i = 10,1,-1 + do + emu.write(1280+i-1, string.byte(partial, (second-1)*10+i), emu.memType.cpu) + end + while timestamp == timestampLast do + timestamp = os.time(os.date("!*t")) + socket.sleep(0.1) + end + timestampLast = timestamp +end + +-- Run at beginning of frame +function handleFrame() + emu.write(0, 1, emu.memType.cpu) + second = second + 1 + local timestampNow = os.time(os.date("!*t")) + if timestampNow - tcpCheckLastTimestamp > 59 then + tcpCheckLastTimestamp = timestampNow + partial = getISSData() + second = 1 + end + if string.len(partial) > 600 then + writeSecondData(partial) + else + writeBlankFrame() + end +end + +-- Per frame data +emu.addEventCallback(handleFrame, emu.eventType.startFrame) diff --git a/bin/iss-nes.nes b/bin/nes/iss-nes.nes similarity index 86% rename from bin/iss-nes.nes rename to bin/nes/iss-nes.nes index 17d9f0b..5df0a16 100644 Binary files a/bin/iss-nes.nes and b/bin/nes/iss-nes.nes differ diff --git a/bin/server-linux-386 b/bin/server-linux-386 deleted file mode 100755 index 8481ee2..0000000 Binary files a/bin/server-linux-386 and /dev/null differ diff --git a/bin/server-linux-amd64 b/bin/server-linux-amd64 deleted file mode 100755 index 0403745..0000000 Binary files a/bin/server-linux-amd64 and /dev/null differ diff --git a/bin/server-linux-arm b/bin/server-linux-arm deleted file mode 100755 index 554fd4e..0000000 Binary files a/bin/server-linux-arm and /dev/null differ diff --git a/bin/server-linux-arm64 b/bin/server-linux-arm64 deleted file mode 100755 index b94bfae..0000000 Binary files a/bin/server-linux-arm64 and /dev/null differ diff --git a/bin/server-windows-386.exe b/bin/server-windows-386.exe deleted file mode 100755 index b7df7ac..0000000 Binary files a/bin/server-windows-386.exe and /dev/null differ diff --git a/bin/server-windows-amd64.exe b/bin/server-windows-amd64.exe deleted file mode 100755 index 4140ee3..0000000 Binary files a/bin/server-windows-amd64.exe and /dev/null differ diff --git a/iss-nes-TAStm32/iss-nes-TAStm32.go b/iss-nes-TAStm32/iss-nes-TAStm32.go new file mode 100644 index 0000000..e2b8ff0 --- /dev/null +++ b/iss-nes-TAStm32/iss-nes-TAStm32.go @@ -0,0 +1,341 @@ +/*- + * Copyright (C) 2020, Vi Grey + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +package main + +import ( + "github.com/mikepb/go-serial" + + "bufio" + "fmt" + "io/ioutil" + "net" + "os" + "os/signal" + "strconv" + "strings" + "syscall" + "time" +) + +const ( + DIALHOST = "vigrey.com" + DIALPORT = "56502" +) + +var ( + ctrlC chan os.Signal + msgQueue []byte + usbSerialConnected bool + device *serial.Port + issDataBuffer, issSecondData []byte + start bool + stop chan bool +) + +/* + * ------------------------------ + * CTRL+C HANDLER CODE (START) + * ------------------------------ + */ + +// Set Ctrl-C handling +func setupCloseHandler() { + ctrlC = make(chan os.Signal) + signal.Notify(ctrlC, os.Interrupt, syscall.SIGTERM) + go func() { + <-ctrlC + os.Exit(0) + }() +} + +/* + * ------------------------------ + * CTRL+C HANDLER CODE (END) + * ------------------------------ + */ + +/* + * ------------------------------ + * ISS DATA CODE (START) + * ------------------------------ + */ + +// Grabs ISS position data from DIALHOST:DIALPORT and set it to the +// issDataBuffer +func tcpGet() { + // Get ISS position data from DIALHOST:DIALPORT + conn, err := net.Dial("tcp", DIALHOST+":"+DIALPORT) + if err == nil { + // Get ISS position data from the request + issDataBufferTmp, err := ioutil.ReadAll(conn) + if err == nil { + if len(issDataBufferTmp) > 600 { + // If DIALHOST:DIALPORT provides more than 60 seconds of + // data, set buffer to the response + issDataBuffer = issDataBufferTmp[:] + } + } + } +} + +// Timer set for 60 seconds continually to grab ISS position data from +// DIALHOST:DIALPORT +func getISSData() { + for { + // Wait 60 seconds before getting data from https://vigrey.com/iss + <-time.After(60 * time.Second) + go tcpGet() + } +} + +// Timer set for 1 second continually to set this second's 10 bytes of +// ISS tracking data from the buffer +func getISSSecondData() { + for { + // Wait 1 second before setting new framePosition + <-time.After(1 * time.Second) + go getISSSecondDataBytes() + } +} + +// Grabs this second's 10 bytes of ISS tracking data +func getISSSecondDataBytes() { + if len(issDataBuffer) < 10 { + // If the buffer length is less than 10, set framePosition to + // default Epoch time values + issSecondData = []byte{0x01, 0x19, 0x70, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00} + } else { + // Set issSecondData to the first 10 bytes of issDataBuffer + // Remove first 10 bytes of buffer + issSecondData = issDataBuffer[:10] + issDataBuffer = issDataBuffer[10:] + } + if usbSerialConnected { + // Set 2 FE pulse bytes at the beginning of issSecondData + issSecondData = append([]byte{0xff, 0xff, 0xff}, issSecondData...) + issSecondDataToMsgBuffer() + go sendMsgQueueChunk() + } +} + +// Set 'A' character in front of each byte of issSecondData and add the +// data to msgQueue +func issSecondDataToMsgBuffer() { + dataForMsgBuffer := []byte{} + for _, b := range issSecondData { + dataForMsgBuffer = append(dataForMsgBuffer, 'A') + dataForMsgBuffer = append(dataForMsgBuffer, b) + } + msgQueue = append(msgQueue, dataForMsgBuffer...) +} + +/* + * ------------------------------ + * ISS DATA CODE (END) + * ------------------------------ + */ + +/* + * ------------------------------ + * USB SERIAL CODE (START) + * ------------------------------ + */ + +// Try to connect to TAStm32 device, if failed to connect, waits 1 +// second and tries connecting again +func connectSerial() { + serialUSBPath := getTAStm32COMPath() + if serialUSBPath != "" { + connectToSerialUSB(serialUSBPath) + } +} + +// Get the Serial Device location to connect to the TAStm32 +func getTAStm32COMPath() string { + infoList, err := serial.ListPorts() + usbSerialPath := "" + if err != nil { + return usbSerialPath + } + for _, list := range infoList { + vid, pid, _ := list.USBVIDPID() + vidStr := strings.ToLower(strconv.FormatInt(int64(vid), 16)) + pidStr := strings.ToLower(strconv.FormatInt(int64(pid), 16)) + if pidStr == "7a5" && vidStr == "b07" { + usbSerialPath = list.Name() + break + } + } + return usbSerialPath +} + +// Connect to TAStm32 device +func connectToSerialUSB(usbSerialPath string) { + usbSerialConnected = false + options := serial.RawOptions + options.Mode = serial.MODE_READ_WRITE + options.BitRate = 115200 + var err error + device, err = options.Open(usbSerialPath) + if err != nil { + printStatus(" Error Connecting to TAStm32 Device!") + return + } + defer device.Close() + _, err = device.WriteString("R") + if err != nil { + fmt.Println(err) + return + } + buf := make([]byte, 2) + _, err = device.Read(buf) + if err != nil { + fmt.Println(err) + return + } + _, err = device.WriteString("SAN\x80\x00") + if err != nil { + fmt.Println(err) + return + } + buf = make([]byte, 2) + _, err = device.Read(buf) + if err != nil { + fmt.Println(err) + return + } + _, err = device.WriteString("A\x00") + if err != nil { + fmt.Println(err) + return + } + usbSerialConnected = true + msgQueue = []byte{} + start = true + stop = make(chan bool) + + <-stop +} + +// Prints status message to STDOUT along with a timestamp +func printStatus(msg string) { + timestamp := time.Now().Format("Jan 02 15:04:05") + fmt.Println(timestamp + " - " + msg) +} + +// SendmsgQueue data from the TAStm32 device to the NES +func sendMsgQueueChunk() { + if device != nil { + if len(msgQueue) >= 2 { + device.Write(msgQueue) + msgQueue = []byte{} + } + } +} + +/* + * ------------------------------ + * USB SERIAL CODE (END) + * ------------------------------ + */ + +/* + * ------------------------------ + * COMMAND ENTER CODE (START) + * ------------------------------ + */ + +func getCommand() { + printHelp() + for { + fmt.Println() + fmt.Print("ENTER COMMAND: ") + r := bufio.NewReader(os.Stdin) + cmdStr, _ := r.ReadString('\n') + cmdStr = strings.ToLower(strings.TrimSuffix(cmdStr, "\n")) + fmt.Println() + if cmdStr == "start" { + if start { + fmt.Println("Cannot Start: TAStm32 Already Started") + } else { + fmt.Println("Starting TAStm32") + go startTAStm32() + } + } else if cmdStr == "restart" { + if start { + fmt.Println("Stopping TAStm32") + stopTAStm32() + } + fmt.Println("Starting TAStm32") + go startTAStm32() + } else if cmdStr == "stop" { + if !start { + fmt.Println("Cannot Stop: TAStm32 Not Started") + } else { + fmt.Println("Stopping TAStm32") + stopTAStm32() + } + } else if cmdStr == "help" { + printHelp() + } else if cmdStr == "quit" { + return + } else { + fmt.Println("Invalid Command") + } + } +} + +func stopTAStm32() { + start = false + usbSerialConnected = false + stop <- true +} + +func startTAStm32() { + connectSerial() +} + +func printHelp() { + fmt.Println("Available Commands: start, restart, stop, quit, help") +} + +/* + * ------------------------------ + * COMMAND ENTER CODE (END) + * ------------------------------ + */ + +func main() { + setupCloseHandler() + tcpGet() + go getISSData() + go getISSSecondData() + go getISSSecondDataBytes() + getCommand() +} diff --git a/iss-nes-TAStm32/udev/51-tastm32.rules b/iss-nes-TAStm32/udev/51-tastm32.rules new file mode 100644 index 0000000..3af7d98 --- /dev/null +++ b/iss-nes-TAStm32/udev/51-tastm32.rules @@ -0,0 +1 @@ +SUBSYSTEM=="tty", ATTRS{idVendor}=="0b07", ATTRS{idProduct}=="07a5", ATTRS{serial}=="0000:00:1a.0", MODE="0666" diff --git a/lua/fceux.lua b/lua/fceux.lua index f003576..7d79df6 100644 --- a/lua/fceux.lua +++ b/lua/fceux.lua @@ -25,19 +25,67 @@ local socket = require 'socket.core' -local HOST = 'localhost' +local HOST = "vigrey.com" local PORT = 56502 +local partial = "" +local second = 0 +local tcpCheckLastTimestamp = 0 +-- TCP request to HOST:PORT for ISS Data +function getISSData() + local tcp = assert(socket.tcp()) + tcp:settimeout(5) + tcp:connect(HOST, PORT) + local s, status, partial = tcp:receive() + tcp:close() + return partial +end + +-- Write all 0s to the 10 input memory bytes and then wait 5 seconds +function writeBlankFrame() + for i = 10,1,-1 + do + memory.writebyte(1280+i-1, 0) + end + socket.sleep(5) + tcpCheckLastTimestamp = 0 + second = 0 +end + +-- Write minute worth of ISS tracking data to input memory bytes +function writeSecondData(partial) + local timestamp = os.time(os.date("!*t")) + local timestampLast = os.time(os.date("!*t")) + for i = 10,1,-1 + do + memory.writebyte(1280+i-1, string.byte(partial, (second-1)*10+i)) + end + while timestamp == timestampLast do + timestamp = os.time(os.date("!*t")) + socket.sleep(0.1) + end + timestampLast = timestamp +end + +-- Run at beginning of frame +function handleFrame() + memory.writebyte(0, 1) + second = second + 1 + local timestampNow = os.time(os.date("!*t")) + if timestampNow - tcpCheckLastTimestamp > 59 then + tcpCheckLastTimestamp = timestampNow + partial = getISSData() + second = 1 + end + if string.len(partial) > 600 then + writeSecondData(partial) + else + writeBlankFrame() + end +end + +-- Per frame data while true do - memory.writebyte(0, 1) - emu.frameadvance() - local tcp = assert(socket.tcp()) - tcp:settimeout(1) - tcp:connect(HOST, PORT) - local s, status, partial = tcp:receive(tcpResLen) - tcp:close() - for i = 10,1,-1 - do - memory.writebyte(1280+i-1, string.byte(partial, i)) - end + handleFrame() + emu.frameadvance() end diff --git a/lua/mesen.lua b/lua/mesen.lua index dd10b2c..a3a2736 100644 --- a/lua/mesen.lua +++ b/lua/mesen.lua @@ -1,4 +1,4 @@ --- Copyright (C) 2020, Vi Grey +-- Copyright (C) 2020, Vi Grey -- All rights reserved. -- -- Redistribution and use in source and binary forms, with or without @@ -25,20 +25,64 @@ local socket = require 'socket.core' -local HOST = 'localhost' +local HOST = "vigrey.com" local PORT = 56502 +local partial = "" +local second = 0 +local tcpCheckLastTimestamp = 0 +-- TCP request to HOST:PORT for ISS Data +function getISSData() + local tcp = assert(socket.tcp()) + tcp:settimeout(5) + tcp:connect(HOST, PORT) + local s, status, partial = tcp:receive() + tcp:close() + return partial +end + +-- Write all 0s to the 10 input memory bytes and then wait 5 seconds +function writeBlankFrame() + for i = 10,1,-1 + do + emu.write(1280+i-1, 0, emu.memType.cpu) + end + socket.sleep(5) + tcpCheckLastTimestamp = 0 + second = 0 +end + +-- Write minute worth of ISS tracking data to input memory bytes +function writeSecondData(partial) + local timestamp = os.time(os.date("!*t")) + local timestampLast = os.time(os.date("!*t")) + for i = 10,1,-1 + do + emu.write(1280+i-1, string.byte(partial, (second-1)*10+i), emu.memType.cpu) + end + while timestamp == timestampLast do + timestamp = os.time(os.date("!*t")) + socket.sleep(0.1) + end + timestampLast = timestamp +end + +-- Run at beginning of frame function handleFrame() - emu.write(0, 1, emu.memType.cpu) - local tcp = assert(socket.tcp()) - tcp:settimeout(1) - tcp:connect(HOST, PORT) - local s, status, partial = tcp:receive(tcpResLen) - tcp:close() - for i = 10,1,-1 - do - emu.write(1280+i-1, string.byte(partial, i), emu.memType.cpu) - end + emu.write(0, 1, emu.memType.cpu) + second = second + 1 + local timestampNow = os.time(os.date("!*t")) + if timestampNow - tcpCheckLastTimestamp > 59 then + tcpCheckLastTimestamp = timestampNow + partial = getISSData() + second = 1 + end + if string.len(partial) > 600 then + writeSecondData(partial) + else + writeBlankFrame() + end end +-- Per frame data emu.addEventCallback(handleFrame, emu.eventType.startFrame) diff --git a/server/server.go b/server/server.go deleted file mode 100644 index a7ac358..0000000 --- a/server/server.go +++ /dev/null @@ -1,92 +0,0 @@ -package main - -import ( - "fmt" - "io/ioutil" - "net" - "net/http" - "time" -) - -const ( - HOST = "localhost" - PORT = "56502" -) - -var ( - positionBuffer []byte - framePosition []byte -) - -func main() { - httpGet() - getFramePosition() - go getISSData() - go getFrameData() - startServer() -} - -func httpGet() { - // Get iss position data from https://vigrey.com/iss - resp, err := http.Get("https://vigrey.com/iss") - if err == nil { - // Get ISS position data from - positionBufferTmp, err := ioutil.ReadAll(resp.Body) - if err == nil { - if len(positionBufferTmp) > 600 { - // If https://vigrey.com/iss provides more than 60 seconds of - // data, set buffer to the response - positionBuffer = positionBufferTmp[:] - } - } - } -} - -func getISSData() { - for { - // Wait 60 seconds before getting data from https://vigrey.com/iss - <-time.After(60 * time.Second) - go httpGet() - } -} - -func getFrameData() { - for { - // Wait 1 seconds before setting new framePosition - <-time.After(1 * time.Second) - go getFramePosition() - } -} - -func getFramePosition() { - if len(positionBuffer) < 10 { - // If the buffer length is less than 10, set framePosition to - // default Epoch time values - framePosition = []byte{0x01, 0x19, 0x70, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00} - } else { - // Set framePosition to the first 10 bytes of buffer - // Remove first 10 bytes of buffer - framePosition = positionBuffer[:10] - positionBuffer = positionBuffer[10:] - } -} - -func startServer() { - // Start server at HOST:PORT - l, err := net.Listen("tcp", HOST+":"+PORT) - if err == nil { - fmt.Println("Server started at " + HOST + ":" + PORT) - defer l.Close() - for { - // Wait for client to connect to server at HOST:PORT - conn, err := l.Accept() - if err == nil { - // Write framePosition (10 bytes) to client and close - // connection - conn.Write(framePosition) - conn.Close() - } - } - } -} diff --git a/src/controller.asm b/src/controller.asm index b86d16e..64e4d0d 100644 --- a/src/controller.asm +++ b/src/controller.asm @@ -25,79 +25,49 @@ ; Controller1 to Top Bar Data Protocol ; -; 0 -; HHDD|dddd -; --------- -; |||| |||| -; |||| ++++- Day Ones Digit -; ||++------ Day Tens Digit -; ++-------- Hour Tens Digit -; -; 1 -; YYYY|yyyy -; --------- -; |||| |||| -; |||| ++++- Year Hundreds Digit -; ++++------ Year Thousands Digit +; Bit 76543210 First Byte ($0500) +; ||||++++- Day Ones Digit +; ||++----- Day Tens Digit +; ++------- Hour Tens Digit ; -; 2 -; YYYY|yyyy -; --------- -; |||| |||| -; |||| ++++- Year Ones Digit -; ++++------ Year Tens Digit +; Bit 76543210 Second Byte ($0501) +; ||||++++- Year Hundreds Digit +; ++++----- Year Thousands Digit ; -; 3 -; hhhh|MMMM -; --------- -; |||| |||| -; |||| ++++- Month Offset Minus 1 -; ++++------ Hour Ones Digit +; Bit 76543210 Third Byte ($0502) +; ||||++++- Year Ones Digit +; ++++----- Year Tens Digit ; -; 4 -; NTTT|tttt -; --------- -; |||| |||| -; |||| ++++- Latitude Ones Place -; |+++------ Latitude Tens Place -; +--------- Longitude Hundreds Digit +; Bit 76543210 Fourth Byte ($0503) +; ||||++++- Month Offset Minus 1 +; ++++----- Hour Ones Digit ; -; 5 -; TTTT|tttt -; --------- -; |||| |||| -; |||| ++++- Latitude Hundredths Place -; ++++------ Latitude Tenths Place +; Bit 76543210 Fifth Byte ($0504) +; ||||++++- Latitude Ones Digit +; |+++----- Latitude Tens Digit +; +-------- Longitude Hundreds Digit ; -; 6 -; NNNN|nnnn -; --------- -; |||| |||| -; |||| ++++- Longitude Ones Place -; ++++------ Longitude Tens Place +; Bit 76543210 Sixth Byte ($0505) +; ||||++++- Latitude Hundredths Place +; ++++----- Latitude Tenths Place ; -; 7 -; NNNN|nnnn -; --------- -; |||| |||| -; |||| ++++- Longitude Hundredths Place -; ++++------ Longitude Tenths Place - -; 8 -; TSSS|ssss -; --------- -; |||| |||| -; |||| ++++- Second Ones Digit -; |+++------ Second Tens Digit -; +--------- Latitude Hemisphere +; Bit 76543210 Seventh Byte ($0506) +; ||||++++- Longitude Ones Digit +; ++++----- Longitude Tens Digit +; +; Bit 76543210 Eighth Byte ($0507) +; ||||++++- Longitude Hundredths Place +; ++++----- Longitude Tenths Place +; +; Bit 76543210 Ninth Byte ($0508) +; ||||++++- Second Ones Digit +; |+++----- Second Tens Digit +; +-------- Latitude Hemisphere (0=North/1=South) ; -; 9 -; NMMM|mmmm -; --------- -; |||| |||| -; |||| ++++- Minute Ones Digit -; |+++------ Minute Tens Digit -; +--------- Longitude Hemisphere +; Bit 76543210 Tenth Byte ($0509) +; ||||++++- Minute Ones Digit +; |+++----- Minute Tens Digit +; +-------- Longitude Hemisphere (0=East/1=West) SetDefaultDate: lda #$01 @@ -111,7 +81,6 @@ SetDefaultDate: PollControllerSync: lda #$00 sta controllervalid - jsr SetDefaultDate ldx #$06 ldy #$08 PollControllerSyncLatch: @@ -121,7 +90,7 @@ PollControllerSyncLatch: sta CONTROLLER1 PollControllerSyncLoop: lda CONTROLLER1 - lsr A + lsr ror controllersync dey bne PollControllerSyncLoop @@ -130,6 +99,7 @@ PollControllerSyncLoop: beq PollControllerSyncLoopIsFF cpx #$06 beq PollControllerSyncContinue + ; FE sync pulse is required at least once per frame lda #$01 sta controllervalid jmp PollControllerSyncContinue @@ -139,8 +109,14 @@ PollControllerSyncLoopIsFF: bne PollControllerSyncLatch PollControllerSyncContinue: lda controllervalid - beq PollControllerSyncDone - jsr PollController + beq PollControllerSyncInvalid + lda controllersync + sta controller1 + ldx #$01 + ldy #$08 + jmp PollControllerLatch +PollControllerSyncInvalid: + jsr SetController1LastFrameToController1 PollControllerSyncDone: jsr PollControllerFinished rts @@ -155,15 +131,14 @@ PollControllerLatch: sta CONTROLLER1 PollController1Loop: lda CONTROLLER1 - lsr A - rol controller1, x + lsr + rol controller1, X dey bne PollController1Loop ldy #$08 inx cpx #$0A bne PollControllerLatch - rts PollControllerFinished: jsr GetDate jsr GetTime @@ -645,3 +620,23 @@ LongitudeToXScrollContinue: sta nametable LongitudeToXScrollDone: rts + +SetController1ToController1LastFrame: + ldy #$00 +SetController1ToController1LastFrameLoop: + lda (controller1), Y + sta (controller1LastFrame), Y + iny + cpy #$0A + bcc SetController1ToController1LastFrameLoop + rts + +SetController1LastFrameToController1: + ldy #$00 +SetController1LastFrameToController1Loop: + lda (controller1LastFrame), Y + sta (controller1), Y + iny + cpy #$0A + bcc SetController1LastFrameToController1Loop + rts diff --git a/src/prg.asm b/src/prg.asm index 6e433a9..0d13ed2 100644 --- a/src/prg.asm +++ b/src/prg.asm @@ -96,12 +96,12 @@ Sprite0HitWait: sta PPU_CTRL lda xscroll sta PPU_SCROLL - NMIDone: jsr Update rti Update: + jsr SetController1ToController1LastFrame lda lua beq UpdateNotLua jsr PollControllerFinished diff --git a/src/ram.asm b/src/ram.asm index e4ae1d1..bbf4240 100644 --- a/src/ram.asm +++ b/src/ram.asm @@ -63,6 +63,7 @@ TopBarVariables: .enum $0500 controller1 dsb 10 + controller1LastFrame dsb 10 controllersync dsb 1 controllervalid dsb 1 .ende