diff --git a/arduino-lib/Apple2Idiot/A2I_commands.h b/arduino-lib/Apple2Idiot/A2I_commands.h index 60ef6f3..f856344 100644 --- a/arduino-lib/Apple2Idiot/A2I_commands.h +++ b/arduino-lib/Apple2Idiot/A2I_commands.h @@ -37,9 +37,10 @@ # PROGRAM IDS # ################################################*/ -#define APP_WEATHER 200 -#define APP_CHESS 201 -#define APP_ISS 202 +#define APP_WEATHER 200 +#define APP_CHESS 201 +#define APP_ISS 202 +#define APP_DND5EAPI 202 /*################################################ diff --git a/card/apple2idiot_new/apple2idiot_new.kicad_pro b/card/apple2idiot_new/apple2idiot_new.kicad_pro index 7a53b02..23e6993 100644 --- a/card/apple2idiot_new/apple2idiot_new.kicad_pro +++ b/card/apple2idiot_new/apple2idiot_new.kicad_pro @@ -309,7 +309,7 @@ "workbook_filename": "" }, "page_layout_descr_file": "", - "plot_directory": "", + "plot_directory": "/home/equant/Sync/projects/apple_ii/kfest_2022/Presentation/assets/", "spice_adjust_passive_values": false, "spice_external_command": "spice \"%I\"", "subpart_first_id": 65, diff --git a/card/apple2idiot_new/apple2idiot_new.pdf b/card/apple2idiot_new/apple2idiot_new.pdf new file mode 100644 index 0000000..1dc8400 Binary files /dev/null and b/card/apple2idiot_new/apple2idiot_new.pdf differ diff --git a/cc65-lib/apple2idiot.c b/cc65-lib/apple2idiot.c index f1d885e..91b08a6 100644 --- a/cc65-lib/apple2idiot.c +++ b/cc65-lib/apple2idiot.c @@ -5,7 +5,7 @@ #include "globals.h" #include "apple2idiot.h" -#include "../../../../arduino-lib/Apple2Idiot/A2I_commands.h" +#include "A2I_commands.h" #define MAX_STR_LEN 250 diff --git a/examples/20-multiapp-arduino/20-multiapp-arduino.ino b/examples/20-multiapp-arduino/20-multiapp-arduino.ino index 4aca248..ca78d05 100644 --- a/examples/20-multiapp-arduino/20-multiapp-arduino.ino +++ b/examples/20-multiapp-arduino/20-multiapp-arduino.ino @@ -7,17 +7,15 @@ github.com/equant // Load Wi-Fi library #include #include -//#include -//#include #include "credentials.h" #include "a2i_weather.h" #include "a2i_iss.h" +#include "a2i_chess.h" #define AUTO_CONNECT_TO_WIFI 1 #define AUTO_CONNECT_TIMEOUT 5 Apple2Idiot a2i = Apple2Idiot(); -//HTTPClient http; /*################################################ # Applications we're going to support # @@ -33,13 +31,12 @@ Apple2Idiot a2i = Apple2Idiot(); # app. ################################################*/ -//Chess chess_app = Chess(); +Chess chess_app = Chess(); Weather weather_app = Weather(); Iss iss_app = Iss(); -#define N_APPS 2 -//byte app_ids[N_APPS] = {APP_WEATHER, APP_ISS}; -byte app_ids[N_APPS] = {APP_ISS, APP_WEATHER}; +#define N_APPS 3 +byte app_ids[N_APPS] = {APP_ISS, APP_WEATHER, APP_CHESS}; /*******************/ /* Variables */ @@ -155,6 +152,13 @@ void loop() { a2i.write_data(APPLE_COMMAND_ADDRESS, ACK); //a2i.write_data(ESP_COMMAND_ADDRESS, EOT); // notify Apple IIe we are done processing command byte } + else if (current_app_id == chess_app.appId) { + Serial.println("Received a command for Chess()"); + chess_app.handleCommand(command_byte); + Serial.println("...command for Chess() handled"); + a2i.write_data(APPLE_COMMAND_ADDRESS, ACK); + //a2i.write_data(ESP_COMMAND_ADDRESS, EOT); // notify Apple IIe we are done processing command byte + } else { Serial.println("Received a command for UNKNOWN()"); } diff --git a/examples/20-multiapp-arduino/README.md b/examples/20-multiapp-arduino/README.md index aebfb63..9ea3848 100644 --- a/examples/20-multiapp-arduino/README.md +++ b/examples/20-multiapp-arduino/README.md @@ -4,4 +4,5 @@ For now, this works with both the basic and and cc65 clients `../15_fixed_essid_weather-basic` `../15_fixed_essid_weather-cc65` + if arduino-cli compile --fqbn esp32:esp32:nodemcu-32s .; then arduino-cli upload -p /dev/ttyUSB0 --fqbn esp32:esp32:esp32-poe-iso .; fi diff --git a/examples/20-multiapp-arduino/a2i_chess.cpp b/examples/20-multiapp-arduino/a2i_chess.cpp index c568396..bbc787a 100644 --- a/examples/20-multiapp-arduino/a2i_chess.cpp +++ b/examples/20-multiapp-arduino/a2i_chess.cpp @@ -30,13 +30,13 @@ byte Chess::handleCommand(byte command) { } a2i->write_data(ESP_COMMAND_ADDRESS, result); a2i->read_ram(11); - break; + return ACK; } case CHESS_NEW_GAME: { a2i->write_data(ESP_COMMAND_ADDRESS, ACK); // notify Apple IIe we are processing command byte Serial.println("CHESS_NEW_GAME"); strcpy(game_string, ""); - break; + return ACK; } default: { return COMMAND_NOT_FOUND; @@ -55,6 +55,8 @@ byte Chess::makeMove(String move_string) { * getAIMove() Then we get the AI move. * Then we check the status and return (did someone win or lose?) */ + Apple2Idiot a2i; + HTTPClient http; if (validateMove(move_string) == CHESS_VALID_MOVE) { strcat(game_string, move_string.c_str()); } else { @@ -97,17 +99,19 @@ byte Chess::makeMove(String move_string) { } char* Chess::getGameStatus(char* game_string) { + Apple2Idiot a2i; + HTTPClient http; Serial.print("getGameStatus() "); Serial.println(game_status); char api_request[MAX_STR_LEN]; sprintf(api_request, "%s/status/%s", api_entry_point, game_string); Serial.print(" "); Serial.println(api_request); - http->begin(api_request); - int httpCode = http->GET(); //Make the request + http.begin(api_request); + int httpCode = http.GET(); //Make the request delay(10); if (httpCode > 0) { //Check for the returning code Serial.println(" Success on HTTP request"); - String payload = http->getString(); + String payload = http.getString(); //Serial.println("++++++++++++++++++++++++"); //Serial.println(payload); //Serial.println("++++++++++++++++++++++++"); @@ -135,17 +139,19 @@ char* Chess::getGameStatus(char* game_string) { } char* Chess::getAIMove() { + Apple2Idiot a2i; + HTTPClient http; Serial.print("getAIMove() "); Serial.println(game_status); char api_request[MAX_STR_LEN]; sprintf(api_request, "%s/next_best/%s", api_entry_point, game_string); Serial.print(" "); Serial.println(api_request); - http->begin(api_request); - int httpCode = http->GET(); //Make the request + http.begin(api_request); + int httpCode = http.GET(); //Make the request delay(10); if (httpCode > 0) { //Check for the returning code Serial.println(" Success on HTTP request"); - String payload = http->getString(); + String payload = http.getString(); //Serial.println("++++++++++++++++++++++++"); //Serial.println(payload); //Serial.println("++++++++++++++++++++++++"); @@ -173,16 +179,18 @@ char* Chess::getAIMove() { } byte Chess::validateMove(String move_string) { + Apple2Idiot a2i; + HTTPClient http; Serial.print("validateMove() "); Serial.println(move_string); char api_request[MAX_STR_LEN]; sprintf(api_request, "%s/valid_move/%s%s", api_entry_point, game_string, move_string); Serial.print(" "); Serial.println(api_request); - http->begin(api_request); - int httpCode = http->GET(); //Make the request + http.begin(api_request); + int httpCode = http.GET(); //Make the request delay(10); if (httpCode > 0) { //Check for the returning code Serial.println(" Success on HTTP request"); - String payload = http->getString(); + String payload = http.getString(); //Serial.println("++++++++++++++++++++++++"); //Serial.println(payload); //Serial.println("++++++++++++++++++++++++"); @@ -226,16 +234,18 @@ void Chess::removeSubstr (char *string, char *sub) { } void Chess::getBoard() { + Apple2Idiot a2i; + HTTPClient http; Serial.println("getBoard() "); char api_request[MAX_STR_LEN]; sprintf(api_request, "%s/board_string/%s", api_entry_point, game_string); Serial.print(" "); Serial.println(api_request); - http->begin(api_request); - int httpCode = http->GET(); //Make the request + http.begin(api_request); + int httpCode = http.GET(); //Make the request delay(10); if (httpCode > 0) { //Check for the returning code Serial.println(" Success on HTTP request"); - String payload = http->getString(); + String payload = http.getString(); //Serial.println("++++++++++++++++++++++++"); //Serial.println(payload); //Serial.println("++++++++++++++++++++++++"); diff --git a/examples/20-multiapp-arduino/a2i_weather.h b/examples/20-multiapp-arduino/a2i_weather.h index 28703c2..79eb8fe 100644 --- a/examples/20-multiapp-arduino/a2i_weather.h +++ b/examples/20-multiapp-arduino/a2i_weather.h @@ -1,5 +1,5 @@ -#ifndef A2I_CHESS_H -#define A2I_CHESS_H +#ifndef A2I_WEATHER_H +#define A2I_WEATHER_H #include #include diff --git a/examples/20-multiapp-arduino/iss-tracker/.gitignore b/examples/20-multiapp-arduino/iss-tracker/.gitignore new file mode 100644 index 0000000..1958a24 --- /dev/null +++ b/examples/20-multiapp-arduino/iss-tracker/.gitignore @@ -0,0 +1,12 @@ +iss.xex +json_test +iss.s +iss.o +nsio.s +nsio.o +faux_json.s +faux_json.o +app_key.s +app_key.o +colors.s +colors.o diff --git a/examples/20-multiapp-arduino/iss-tracker/apple2/Makefile b/examples/20-multiapp-arduino/iss-tracker/apple2/Makefile new file mode 100644 index 0000000..521901c --- /dev/null +++ b/examples/20-multiapp-arduino/iss-tracker/apple2/Makefile @@ -0,0 +1,353 @@ +############################################################################### +### Generic Makefile for cc65 projects - full version with abstract options ### +### V1.3.0(w) 2010 - 2013 Oliver Schmidt & Patryk "Silver Dream !" Łogiewa ### +############################################################################### + +############################################################################### +### In order to override defaults - values can be assigned to the variables ### +############################################################################### + +# Space or comma separated list of cc65 supported target platforms to build for. +# Default: c64 (lowercase!) +TARGETS := apple2 + +# Name of the final, single-file executable. +# Default: name of the current dir with target name appended +PROGRAM := iss + +# Path(s) to additional libraries required for linking the program +# Use only if you don't want to place copies of the libraries in SRCDIR +# Default: none +LIBS := + +# Custom linker issuration file +# Use only if you don't want to place it in SRCDIR +# Default: none +CONFIG := + +# Additional C compiler flags and options. +# Default: none +CFLAGS = -Os --static-locals -DBUILD_APPLE2 + +# Additional assembler flags and options. +# Default: none +ASFLAGS = + +# Additional linker flags and options. +# Default: none +LDFLAGS = --start-addr 0x4000 --ld-args -D,__HIMEM__=0xBF00 apple2-iobuf-0800.o + +# Path to the directory containing C and ASM sources. +# Default: src +SRCDIR := + +# Path to the directory where object files are to be stored (inside respective target subdirectories). +# Default: obj +OBJDIR := + +# Command used to run the emulator. +# Default: depending on target platform. For default (c64) target: x64 -kernal kernal -VICIIdsize -autoload +EMUCMD := + +# Optional commands used before starting the emulation process, and after finishing it. +# Default: none +#PREEMUCMD := osascript -e "tell application \"System Events\" to set isRunning to (name of processes) contains \"X11.bin\"" -e "if isRunning is true then tell application \"X11\" to activate" +#PREEMUCMD := osascript -e "tell application \"X11\" to activate" +#POSTEMUCMD := osascript -e "tell application \"System Events\" to tell process \"X11\" to set visible to false" +#POSTEMUCMD := osascript -e "tell application \"Terminal\" to activate" +PREEMUCMD := +POSTEMUCMD := + +# On Windows machines VICE emulators may not be available in the PATH by default. +# In such case, please set the variable below to point to directory containing +# VICE emulators. +#VICE_HOME := "C:\Program Files\WinVICE-2.2-x86\" +VICE_HOME := + +# Options state file name. You should not need to change this, but for those +# rare cases when you feel you really need to name it differently - here you are +STATEFILE := Makefile.options + +################################################################################### +#### DO NOT EDIT BELOW THIS LINE, UNLESS YOU REALLY KNOW WHAT YOU ARE DOING! #### +################################################################################### + +################################################################################### +### Mapping abstract options to the actual compiler, assembler and linker flags ### +### Predefined compiler, assembler and linker flags, used with abstract options ### +### valid for 2.14.x. Consult the documentation of your cc65 version before use ### +################################################################################### + +# Compiler flags used to tell the compiler to optimise for SPEED +define _optspeed_ + CFLAGS += -Oris +endef + +# Compiler flags used to tell the compiler to optimise for SIZE +define _optsize_ + CFLAGS += -Or +endef + +# Compiler and assembler flags for generating listings +define _listing_ + CFLAGS += --listing $$(@:.o=.lst) + ASFLAGS += --listing $$(@:.o=.lst) + REMOVES += $(addsuffix .lst,$(basename $(OBJECTS))) +endef + +# Linker flags for generating map file +define _mapfile_ + LDFLAGS += --mapfile $$@.map + REMOVES += $(PROGRAM).map +endef + +# Linker flags for generating VICE label file +define _labelfile_ + LDFLAGS += -Ln $$@.lbl + REMOVES += $(PROGRAM).lbl +endef + +# Linker flags for generating a debug file +define _debugfile_ + LDFLAGS += -Wl --dbgfile,$$@.dbg + REMOVES += $(PROGRAM).dbg +endef + +############################################################################### +### Defaults to be used if nothing defined in the editable sections above ### +############################################################################### + +# Presume the C64 target like the cl65 compile & link utility does. +# Set TARGETS to override. +ifeq ($(TARGETS),) + TARGETS := c64 +endif + +# Presume we're in a project directory so name the program like the current +# directory. Set PROGRAM to override. +ifeq ($(PROGRAM),) + PROGRAM := $(notdir $(CURDIR)) +endif + +# Presume the C and asm source files to be located in the subdirectory 'src'. +# Set SRCDIR to override. +ifeq ($(SRCDIR),) + SRCDIR := src +endif + +# Presume the object and dependency files to be located in the subdirectory +# 'obj' (which will be created). Set OBJDIR to override. +ifeq ($(OBJDIR),) + OBJDIR := obj +endif +TARGETOBJDIR := $(OBJDIR)/$(TARGETS) + +# On Windows it is mandatory to have CC65_HOME set. So do not unnecessarily +# rely on cl65 being added to the PATH in this scenario. +ifdef CC65_HOME + CC := $(CC65_HOME)/bin/cl65 +else + CC := cl65 +endif + +# Default emulator commands and options for particular targets. +# Set EMUCMD to override. +c64_EMUCMD := $(VICE_HOME)xscpu64 -VICIIdsize -autostart +c128_EMUCMD := $(VICE_HOME)x128 -kernal kernal -VICIIdsize -autoload +vic20_EMUCMD := $(VICE_HOME)xvic -kernal kernal -VICdsize -autoload +pet_EMUCMD := $(VICE_HOME)xpet -Crtcdsize -autoload +plus4_EMUCMD := $(VICE_HOME)xplus4 -TEDdsize -autoload +# So far there is no x16 emulator in VICE (why??) so we have to use xplus4 with -memsize option +c16_EMUCMD := $(VICE_HOME)xplus4 -ramsize 16 -TEDdsize -autoload +cbm510_EMUCMD := $(VICE_HOME)xcbm2 -model 510 -VICIIdsize -autoload +cbm610_EMUCMD := $(VICE_HOME)xcbm2 -model 610 -Crtcdsize -autoload +atari_EMUCMD := atari800 -windowed -xl -pal -nopatchall -run + +ifeq ($(EMUCMD),) + EMUCMD = $($(CC65TARGET)_EMUCMD) +endif + +############################################################################### +### The magic begins ### +############################################################################### + +# The "Native Win32" GNU Make contains quite some workarounds to get along with +# cmd.exe as shell. However it does not provide means to determine that it does +# actually activate those workarounds. Especially does $(SHELL) NOT contain the +# value 'cmd.exe'. So the usual way to determine if cmd.exe is being used is to +# execute the command 'echo' without any parameters. Only cmd.exe will return a +# non-empy string - saying 'ECHO is on/off'. +# +# Many "Native Win32" prorams accept '/' as directory delimiter just fine. How- +# ever the internal commands of cmd.exe generally require '\' to be used. +# +# cmd.exe has an internal command 'mkdir' that doesn't understand nor require a +# '-p' to create parent directories as needed. +# +# cmd.exe has an internal command 'del' that reports a syntax error if executed +# without any file so make sure to call it only if there's an actual argument. +ifeq ($(shell echo),) + MKDIR = mkdir -p $1 + RMDIR = rmdir $1 + RMFILES = $(RM) $1 +else + MKDIR = mkdir $(subst /,\,$1) + RMDIR = rmdir $(subst /,\,$1) + RMFILES = $(if $1,del /f $(subst /,\,$1)) +endif +COMMA := , +SPACE := $(N/A) $(N/A) +define NEWLINE + + +endef +# Note: Do not remove any of the two empty lines above ! + +TARGETLIST := $(subst $(COMMA),$(SPACE),$(TARGETS)) + +ifeq ($(words $(TARGETLIST)),1) + +# Set PROGRAM to something like 'myprog.c64'. +override PROGRAM := $(PROGRAM) + +# Set SOURCES to something like 'src/foo.c src/bar.s'. +# Use of assembler files with names ending differently than .s is deprecated! +SOURCES := $(wildcard $(SRCDIR)/*.c) +SOURCES += $(wildcard $(SRCDIR)/*.s) +SOURCES += $(wildcard $(SRCDIR)/*.asm) +SOURCES += $(wildcard $(SRCDIR)/*.a65) + +# Add to SOURCES something like 'src/c64/me.c src/c64/too.s'. +# Use of assembler files with names ending differently than .s is deprecated! +SOURCES += $(wildcard $(SRCDIR)/$(TARGETLIST)/*.c) +SOURCES += $(wildcard $(SRCDIR)/$(TARGETLIST)/*.s) +SOURCES += $(wildcard $(SRCDIR)/$(TARGETLIST)/*.asm) +SOURCES += $(wildcard $(SRCDIR)/$(TARGETLIST)/*.a65) + +# Set OBJECTS to something like 'obj/c64/foo.o obj/c64/bar.o'. +OBJECTS := $(addsuffix .o,$(basename $(addprefix $(TARGETOBJDIR)/,$(notdir $(SOURCES))))) + +# Set DEPENDS to something like 'obj/c64/foo.d obj/c64/bar.d'. +DEPENDS := $(OBJECTS:.o=.d) + +# Add to LIBS something like 'src/foo.lib src/c64/bar.lib'. +LIBS += $(wildcard $(SRCDIR)/*.lib) +LIBS += $(wildcard $(SRCDIR)/$(TARGETLIST)/*.lib) + +# Add to CONFIG something like 'src/c64/bar.cfg src/foo.cfg'. +CONFIG += $(wildcard $(SRCDIR)/$(TARGETLIST)/*.cfg) +CONFIG += $(wildcard $(SRCDIR)/*.cfg) + +# Select CONFIG file to use. Target specific isss have higher priority. +ifneq ($(word 2,$(CONFIG)),) + CONFIG := $(firstword $(CONFIG)) + $(info Using iss file $(CONFIG) for linking) +endif + +.SUFFIXES: +.PHONY: all test clean zap love + +all: $(PROGRAM) + +-include $(DEPENDS) +-include $(STATEFILE) + +# If OPTIONS are given on the command line then save them to STATEFILE +# if (and only if) they have actually changed. But if OPTIONS are not +# given on the command line then load them from STATEFILE. Have object +# files depend on STATEFILE only if it actually exists. +ifeq ($(origin OPTIONS),command line) + ifneq ($(OPTIONS),$(_OPTIONS_)) + ifeq ($(OPTIONS),) + $(info Removing OPTIONS) + $(shell $(RM) $(STATEFILE)) + $(eval $(STATEFILE):) + else + $(info Saving OPTIONS=$(OPTIONS)) + $(shell echo _OPTIONS_=$(OPTIONS) > $(STATEFILE)) + endif + $(eval $(OBJECTS): $(STATEFILE)) + endif +else + ifeq ($(origin _OPTIONS_),file) + $(info Using saved OPTIONS=$(_OPTIONS_)) + OPTIONS = $(_OPTIONS_) + $(eval $(OBJECTS): $(STATEFILE)) + endif +endif + +# Transform the abstract OPTIONS to the actual cc65 options. +$(foreach o,$(subst $(COMMA),$(SPACE),$(OPTIONS)),$(eval $(_$o_))) + +# Strip potential variant suffix from the actual cc65 target. +CC65TARGET := $(firstword $(subst .,$(SPACE),$(TARGETLIST))) + +# The remaining targets. +$(TARGETOBJDIR): + $(call MKDIR,$@) + +vpath %.c $(SRCDIR)/$(TARGETLIST) $(SRCDIR) + +$(TARGETOBJDIR)/%.o: %.c | $(TARGETOBJDIR) + $(CC) -t $(CC65TARGET) -c --create-dep $(@:.o=.d) $(CFLAGS) -o $@ $< + +vpath %.s $(SRCDIR)/$(TARGETLIST) $(SRCDIR) + +$(TARGETOBJDIR)/%.o: %.s | $(TARGETOBJDIR) + $(CC) -t $(CC65TARGET) -c --create-dep $(@:.o=.d) $(ASFLAGS) -o $@ $< + +vpath %.asm $(SRCDIR)/$(TARGETLIST) $(SRCDIR) + +$(TARGETOBJDIR)/%.o: %.asm | $(TARGETOBJDIR) + $(CC) -t $(CC65TARGET) -c --create-dep $(@:.o=.d) $(ASFLAGS) -o $@ $< + +vpath %.a65 $(SRCDIR)/$(TARGETLIST) $(SRCDIR) + +$(TARGETOBJDIR)/%.o: %.a65 | $(TARGETOBJDIR) + $(CC) -t $(CC65TARGET) -c --create-dep $(@:.o=.d) $(ASFLAGS) -o $@ $< + +$(PROGRAM): $(CONFIG) $(OBJECTS) $(LIBS) + $(CC) -t $(CC65TARGET) $(LDFLAGS) -o $@ $(patsubst %.cfg,-C %.cfg,$^) + +test: $(PROGRAM) + $(PREEMUCMD) + $(EMUCMD) $< + $(POSTEMUCMD) + +dist: $(PROGRAM) + cp dist.apple2/bootable.po dist.apple2/dist.po + java -jar dist.apple2/ac.jar -p dist.apple2/dist.po iss.system sys $(STATEFILE)) + endif + $(eval $(OBJECTS): $(STATEFILE)) + endif +else + ifeq ($(origin _OPTIONS_),file) + $(info Using saved OPTIONS=$(_OPTIONS_)) + OPTIONS = $(_OPTIONS_) + $(eval $(OBJECTS): $(STATEFILE)) + endif +endif + +# Transform the abstract OPTIONS to the actual cc65 options. +$(foreach o,$(subst $(COMMA),$(SPACE),$(OPTIONS)),$(eval $(_$o_))) + +# Strip potential variant suffix from the actual cc65 target. +CC65TARGET := $(firstword $(subst .,$(SPACE),$(TARGETLIST))) + +# The remaining targets. +$(TARGETOBJDIR): + $(call MKDIR,$@) + +vpath %.c $(SRCDIR)/$(TARGETLIST) $(SRCDIR) + +$(TARGETOBJDIR)/%.o: %.c | $(TARGETOBJDIR) + $(CC) -t $(CC65TARGET) -c --create-dep $(@:.o=.d) $(CFLAGS) -o $@ $< + +vpath %.s $(SRCDIR)/$(TARGETLIST) $(SRCDIR) + +$(TARGETOBJDIR)/%.o: %.s | $(TARGETOBJDIR) + $(CC) -t $(CC65TARGET) -c --create-dep $(@:.o=.d) $(ASFLAGS) -o $@ $< + +vpath %.asm $(SRCDIR)/$(TARGETLIST) $(SRCDIR) + +$(TARGETOBJDIR)/%.o: %.asm | $(TARGETOBJDIR) + $(CC) -t $(CC65TARGET) -c --create-dep $(@:.o=.d) $(ASFLAGS) -o $@ $< + +vpath %.a65 $(SRCDIR)/$(TARGETLIST) $(SRCDIR) + +$(TARGETOBJDIR)/%.o: %.a65 | $(TARGETOBJDIR) + $(CC) -t $(CC65TARGET) -c --create-dep $(@:.o=.d) $(ASFLAGS) -o $@ $< + +$(PROGRAM): $(CONFIG) $(OBJECTS) $(LIBS) + $(CC) -t $(CC65TARGET) $(LDFLAGS) -o $@ $(patsubst %.cfg,-C %.cfg,$^) + +test: $(PROGRAM) + $(PREEMUCMD) + $(EMUCMD) $< + $(POSTEMUCMD) + +dist: $(PROGRAM) + cp dist.apple2/bootable.po dist.apple2/dist.po + java -jar dist.apple2/ac.jar -p dist.apple2/dist.po iss.system sys +#include + +// Ethernet driver initialization parameter values +// +#if defined(__APPLE2__) +#define ETH_INIT_DEFAULT 3 // Apple II slot number +#elif defined(__ATARI__) +#define ETH_INIT_DEFAULT 8 // ATARI PBI device ID +#else +#define ETH_INIT_DEFAULT 0 // Unused +#endif + +// Initialize the IP stack +// +// This calls the individual protocol & driver initializations, so this is +// the only *_init routine that must be called by a user application, +// except for dhcp_init which must also be called if the application +// is using DHCP rather than hardcoded IP configuration. +// +// Inputs: eth_init: Ethernet driver initialization parameter +// Output: true if there was an error, false otherwise +// +bool __fastcall__ ip65_init(uint8_t eth_init); + +// Access to Ethernet configuration +// +// Access to the two items below is only valid after ip65_init returned false. +// +extern uint8_t cfg_mac[6]; // MAC address of local machine +extern char eth_name[]; // Zero terminated string containing Ethernet driver name + +// Error codes +// +#define IP65_ERROR_PORT_IN_USE 0x80 +#define IP65_ERROR_TIMEOUT_ON_RECEIVE 0x81 +#define IP65_ERROR_TRANSMIT_FAILED 0x82 +#define IP65_ERROR_TRANSMISSION_REJECTED_BY_PEER 0x83 +#define IP65_ERROR_NAME_TOO_LONG 0x84 +#define IP65_ERROR_DEVICE_FAILURE 0x85 +#define IP65_ERROR_ABORTED_BY_USER 0x86 +#define IP65_ERROR_LISTENER_NOT_AVAILABLE 0x87 +#define IP65_ERROR_CONNECTION_RESET_BY_PEER 0x89 +#define IP65_ERROR_CONNECTION_CLOSED 0x8A +#define IP65_ERROR_MALFORMED_URL 0xA0 +#define IP65_ERROR_DNS_LOOKUP_FAILED 0xA1 + +// Last error code +// +extern uint8_t ip65_error; + +// Convert error code into a string describing the error +// +// The pointer returned is a static string, which mustn't be modified. +// +// Inputs: err_code: Error code +// Output: Zero terminated string describing the error +// +char* __fastcall__ ip65_strerror(uint8_t err_code); + +// Main IP polling loop +// +// This routine should be periodically called by an application at any time +// that an inbound packet needs to be handled. +// It is 'non-blocking', i.e. it will return if there is no packet waiting to be +// handled. Any inbound packet will be handed off to the appropriate handler. +// +// Inputs: None +// Output: true if no packet was waiting or packet handling caused error, false otherwise +// +bool ip65_process(void); + +// Generate a 'random' 16 bit word +// +// Entropy comes from the last ethernet frame, counters, and timer. +// +// Inputs: None +// Output: Pseudo-random 16 bit number +// +uint16_t ip65_random_word(void); + +// Convert 4 octets (IP address, netmask) into a string representing a dotted quad +// +// The string is returned in a statically allocated buffer, which subsequent calls +// will overwrite. +// +// Inputs: quad: IP address +// Output: Zero terminated string containing dotted quad (e.g. "192.168.1.0") +// +char* __fastcall__ dotted_quad(uint32_t quad); + +// Convert a string representing a dotted quad (IP address, netmask) into 4 octets +// +// Inputs: quad: Zero terminated string containing dotted quad (e.g. "192.168.1.0"), +// to simplify URL parsing, a ':' or '/' can also terminate the string. +// Output: IP address, 0 on error +// +uint32_t __fastcall__ parse_dotted_quad(char* quad); + +// Minimal DHCP client implementation +// +// IP addresses are requested from a DHCP server (aka 'leased') but are not renewed +// or released. Although this is not correct behaviour according to the DHCP RFC, +// this works fine in practice in a typical home network environment. +// +// Inputs: None (although ip65_init should be called first) +// Output: false if IP config has been sucesfully obtained and cfg_ip, cfg_netmask, +// cfg_gateway and cfg_dns will be set per response from dhcp server. +// dhcp_server will be set to address of server that provided configuration. +// true if there was an error +// +bool dhcp_init(void); + +// Access to IP configuration +// +// The five items below will be overwritten if dhcp_init is called. +// +extern uint32_t cfg_ip; // IP address of local machine +extern uint32_t cfg_netmask; // Netmask of local network +extern uint32_t cfg_gateway; // IP address of router on local network +extern uint32_t cfg_dns; // IP address of DNS server to use +extern uint32_t dhcp_server; // Address of DHCP server that config was obtained from + +// Resolve a string containing a hostname (or a dotted quad) to an IP address +// +// Inputs: hostname: Zero terminated string containing either a DNS hostname +// (e.g. "host.example.com") or an address in "dotted quad" +// format (e.g. "192.168.1.0") +// Output: IP address of the hostname, 0 on error +// +uint32_t __fastcall__ dns_resolve(const char* hostname); + +// Send a ping (ICMP echo request) to a remote host, and wait for a response +// +// Inputs: dest: Destination IP address +// Output: 0 if no response, otherwise time (in miliseconds) for host to respond +// +uint16_t __fastcall__ icmp_ping(uint32_t dest); + +// Add a UDP listener +// +// Inputs: port: UDP port to listen on +// callback: Vector to call when UDP packet arrives on specified port +// Output: true if too may listeners already installed, false otherwise +// +bool __fastcall__ udp_add_listener(uint16_t port, void (*callback)(void)); + +// Remove a UDP listener +// +// Inputs: port: UDP port to stop listening on +// Output: false if handler found and removed, +// true if handler for specified port not found +// +bool __fastcall__ udp_remove_listener(uint16_t port); + +// Access to received UDP packet +// +// Access to the four items below is only valid in the context of a callback +// added with udp_add_listener. +// +extern uint8_t udp_recv_buf[1476]; // Buffer with data received + uint16_t udp_recv_len(void); // Length of data received + uint32_t udp_recv_src(void); // Source IP address + uint16_t udp_recv_src_port(void); // Source port + +// Send a UDP packet +// +// If the correct MAC address can't be found in the ARP cache then +// an ARP request is sent - and the UDP packet is NOT sent. The caller +// should wait a while calling ip65_process (to allow time for an ARP +// response to arrive) and then call upd_send again. This behavior +// makes sense as a UDP packet may get lost in transit at any time +// so the caller should to be prepared to resend it after a while +// anyway. +// +// Inputs: buf: Pointer to buffer containing data to be sent +// len: Length of data to send (exclusive of any headers) +// dest: Destination IP address +// dest_port: Destination port +// src_port: Source port +// Output: true if an error occured, false otherwise +// +bool __fastcall__ udp_send(const uint8_t* buf, uint16_t len, uint32_t dest, + uint16_t dest_port, uint16_t src_port); + +// Listen for an inbound TCP connection +// +// This is a 'blocking' call, i.e. it will not return until a connection has been made. +// +// Inputs: port: TCP port to listen on +// callback: Vector to call when data arrives on this connection +// buf: Pointer to buffer with data received +// len: -1 on close, otherwise length of data received +// Output: IP address of the connected client, 0 on error +// +uint32_t __fastcall__ tcp_listen(uint16_t port, + void __fastcall__ (*callback)(const uint8_t* buf, + int16_t len)); + +// Make outbound TCP connection +// +// Inputs: dest: Destination IP address +// dest_port: Destination port +// callback: Vector to call when data arrives on this connection +// buf: Pointer to buffer with data received +// len: -1 on close, otherwise length of data received +// Output: true if an error occured, false otherwise +// +bool __fastcall__ tcp_connect(uint32_t dest, uint16_t dest_port, + void __fastcall__ (*callback)(const uint8_t* buf, + int16_t len)); + +// Close the current TCP connection +// +// Inputs: None +// Output: true if an error occured, false otherwise +// +bool tcp_close(void); + +// Send data on the current TCP connection +// +// Inputs: buf: Pointer to buffer containing data to be sent +// len: Length of data to send (up to 1460 bytes) +// Output: true if an error occured, false otherwise +// +bool __fastcall__ tcp_send(const uint8_t* buf, uint16_t len); + +// Send an empty ACK packet on the current TCP connection +// +// Inputs: None +// Output: true if an error occured, false otherwise +// +bool tcp_send_keep_alive(void); + +// Query an SNTP server for current UTC time +// +// Inputs: SNTP server IP address +// Output: The number of seconds since 00:00 on Jan 1 1900 (UTC), 0 on error +// +uint32_t __fastcall__ sntp_get_time(uint32_t server); + +// Download a file from a TFTP server and provide data to user supplied vector +// +// Inputs: server: IP address of server to receive file from +// name: Zero terminated string containing the name of file to download +// callback: Vector to call once for each 512 byte packet received +// buf: Pointer to buffer containing data received +// len: 512 if buffer is full, otherwise number of bytes +// in the buffer +// Output: true if an error occured, false otherwise +// +bool __fastcall__ tftp_download(uint32_t server, const char* name, + void __fastcall__ (*callback)(const uint8_t* buf, + uint16_t len)); + +// Download a file from a TFTP server and provide data to specified memory location +// +// Inputs: server: IP address of server to receive file from +// name: Zero terminated string containing the name of file to download +// buf: Pointer to buffer containing data received +// Output: Length of data received, 0 on error +// +uint16_t __fastcall__ tftp_download_to_memory(uint32_t server, const char* name, + const uint8_t* buf); + +// Upload a file to a TFTP server with data retrieved from user supplied vector +// +// Inputs: server: IP address of server to send file to +// name: Zero terminated string containing the name of file to upload +// callback: Vector to call once for each 512 byte packet to be sent +// buf: Pointer to buffer containing data to be sent +// Output: 512 if buffer is full, otherwise number of bytes +// in the buffer +// Output: true if an error occured, false otherwise +// +bool __fastcall__ tftp_upload(uint32_t server, const char* name, + uint16_t __fastcall__ (*callback)(const uint8_t* buf)); + +// Upload a file to a TFTP server with data retrieved from specified memory location +// +// Inputs: server: IP address of server to send file to +// name: Zero terminated string containing the name of file to upload +// buf: Pointer to buffer containing data to be sent +// len: Length of data to be sent +// Output: true if an error occured, false otherwise +// +bool __fastcall__ tftp_upload_from_memory(uint32_t server, const char* name, + const uint8_t* buf, uint16_t len); + +// Parse an HTTP URL into a form that makes it easy to retrieve the specified resource +// +// On success the variables url_ip, url_port and url_selector (see below) are valid. +// +// Inputs: url: Zero (or ctrl char) terminated string containing the URL +// resolve: Resolve host in URL +// Output: true if an error occured, false otherwise +// +bool __fastcall__ url_parse(const char* url, bool resolve); + +// Access to parsed HTTP URL +// +// Access to the four items below is only valid after url_parse returned false. +// +extern char* url_host; // Zero terminated string containing host in URL + "\r\n\r\n" +extern uint32_t url_ip; // IP address of host in URL (only if 'resolve' is true) +extern uint16_t url_port; // Port number of URL +extern char* url_selector; // Zero terminated string containing selector part of URL + +// Download a resource specified by an HTTP URL +// +// The URL mustn't be longer than 1400 chars. The buffer is temporarily used to hold the +// generated HTTP request so it should have a length of at least 1460 bytes. On success +// the resource is zero terminated. +// +// Inputs: url: Zero (or ctrl char) terminated string containing the URL +// buf: Pointer to a buffer that the resource will be downloaded into +// len: Length of buffer +// Output: Length of resource downloaded, 0 on error +// +uint16_t __fastcall__ url_download(const char* url, const uint8_t* buf, uint16_t len); + +// Start an HTTP server +// +// This routine will stay in an endless loop that is broken only if user press the abort key. +// +// Inputs: port: TCP port to listen on +// callback: Vector to call for each inbound HTTP request +// client: IP address of the client that sent the request +// method: Zero terminated string containing the HTTP method +// path: Zero terminated string containing the HTTP path +// Output: None +// +void __fastcall__ httpd_start(uint16_t port, + void __fastcall__ (*callback)(uint32_t client, + const char* method, + const char* path)); + +// HTTP response types +// +#define HTTPD_RESPONSE_NOHEADER 0 // No HTTP response header +#define HTTPD_RESPONSE_200_TEXT 1 // HTTP Code: 200 OK, Content Type: 'text/text' +#define HTTPD_RESPONSE_200_HTML 2 // HTTP Code: 200 OK, Content Type: 'text/html' +#define HTTPD_RESPONSE_200_DATA 3 // HTTP Code: 200 OK, Content Type: 'application/octet-stream' +#define HTTPD_RESPONSE_404 4 // HTTP Code: 404 Not Found +#define HTTPD_RESPONSE_500 5 // HTTP Code: 500 System Error + +// Send HTTP response +// +// Calling httpd_send_response is only valid in the context of a httpd_start callback. +// For the response types HTTPD_RESPONSE_404 and HTTPD_RESPONSE_500 'buf' is ignored. +// With the response type HTTPD_RESPONSE_NOHEADER it's possible to add more content to +// an already sent HTTP response. +// +// Inputs: response_type: Value describing HTTP code and content type in response header +// buf: Pointer to buffer with HTTP response content +// len: Length of buffer with HTTP response content +// Output: None +// +void __fastcall__ httpd_send_response(uint8_t response_type, + const uint8_t* buf, uint16_t len); + +// Retrieve the value of a variable defined in the previously received HTTP request +// +// Calling http_get_value is only valid in the context of a httpd_start callback. +// Only the first letter in a variable name is significant. E.g. if a querystring contains +// the variables 'a','alpha' and 'alabama', then only the first one will be retrievable. +// +// Inputs: name: Variable to retrieve +// Output: Variable value (zero terminated string) if variable exists, null otherwise. +// +char* __fastcall__ http_get_value(char name); + +// Get number of milliseconds since initialization +// +// Inputs: None +// Output: Current number of milliseconds +// +uint16_t timer_read(void); + +// Check if specified period of time has passed yet +// +// Inputs: time: Number of milliseconds we are willing to wait for +// Output: true if timeout occured, false otherwise +// +bool __fastcall__ timer_timeout(uint16_t time); + +// Check whether the abort key is being pressed +// +// Inputs: None +// Output: true if abort key pressed, false otherwise +// +bool input_check_for_abort_key(void); + +// Control abort key +// +// Control if the user can abort blocking functions with the abort key +// (making them return IP65_ERROR_ABORTED_BY_USER). Initially the abort +// key is enabled. +// +// Inputs: enable: false to disable the key, true to enable the key +// Output: None +// +void __fastcall__ input_set_abort_key(bool enable); + +// Access to actual abort key code +// +extern uint8_t abort_key; + +#endif diff --git a/examples/20-multiapp-arduino/iss-tracker/apple2/src/apple2.ip65/main.c b/examples/20-multiapp-arduino/iss-tracker/apple2/src/apple2.ip65/main.c new file mode 100644 index 0000000..d9e7533 --- /dev/null +++ b/examples/20-multiapp-arduino/iss-tracker/apple2/src/apple2.ip65/main.c @@ -0,0 +1,84 @@ +/** + * ISS Tracker + * + * @author Thomas Cherryhomes + * @email thom dot cherryhomes at gmail dot com + * @license gpl v. 3, see LICENSE.md for details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "map.h" +#include "satellite.h" +#include "osd.h" +#include "ip65.h" + +unsigned char net = ETH_INIT_DEFAULT; +char lon_s[16], lat_s[16]; +int lat, lon; +long timer; +unsigned long ts; + +void error_exit(char *op) +{ + printf("%s:%s\n",op,ip65_strerror(ip65_error)); + cgetc(); + exit(1); +} + +void main(void) +{ + int f; + f = open("ethernet.slot",O_RDONLY); + if (f!=-1) + { + read(f,&net,sizeof(net)); + close(f); + net &= ~'0'; + } + + if (ip65_init(net)) + error_exit("ip65_init"); + + if (dhcp_init()) + error_exit("dhcp_init"); + + tgi_install(tgi_static_stddrv); + tgi_init(); + tgi_apple2_mix(true); + tgi_clear(); + + while (1) + { + timer=524088; + clrscr(); + satellite_fetch(&lon,&lat,lon_s,lat_s,&ts); + map(); + osd(lon_s,lat_s,ts); + satellite(lon,lat); + + while (timer>0) + { + if (kbhit()) + switch(cgetc()) + { + case 0x1b: + return; + case 0x0D: + case 0x0A: + timer=0; + break; + default: + break; + } + + timer--; + } + } +} diff --git a/examples/20-multiapp-arduino/iss-tracker/apple2/src/apple2.ip65/map.h b/examples/20-multiapp-arduino/iss-tracker/apple2/src/apple2.ip65/map.h new file mode 100644 index 0000000..d176ef9 --- /dev/null +++ b/examples/20-multiapp-arduino/iss-tracker/apple2/src/apple2.ip65/map.h @@ -0,0 +1,14 @@ +/** + * ISS Tracker + * + * @author Thomas Cherryhomes + * @email thom dot cherryhomes at gmail dot com + * @license gpl v. 3, see LICENSE.md for details. + */ + +#ifndef MAP_H +#define MAP_H + +void map(void); + +#endif /* MAP_H */ diff --git a/examples/20-multiapp-arduino/iss-tracker/apple2/src/apple2.ip65/osd.h b/examples/20-multiapp-arduino/iss-tracker/apple2/src/apple2.ip65/osd.h new file mode 100644 index 0000000..183d57c --- /dev/null +++ b/examples/20-multiapp-arduino/iss-tracker/apple2/src/apple2.ip65/osd.h @@ -0,0 +1,14 @@ +/** + * ISS Tracker + * + * @author Thomas Cherryhomes + * @email thom dot cherryhomes at gmail dot com + * @license gpl v. 3, see LICENSE.md for details. + */ + +#ifndef OSD_H +#define OSD_H + +void osd(char *lon, char *lat, unsigned long ts); + +#endif /* OSD_H */ diff --git a/examples/20-multiapp-arduino/iss-tracker/apple2/src/apple2.ip65/satellite.c b/examples/20-multiapp-arduino/iss-tracker/apple2/src/apple2.ip65/satellite.c new file mode 100644 index 0000000..50061fb --- /dev/null +++ b/examples/20-multiapp-arduino/iss-tracker/apple2/src/apple2.ip65/satellite.c @@ -0,0 +1,100 @@ +/** + * ISS Tracker + * + * @author Thomas Cherryhomes + * @email thom dot cherryhomes at gmail dot com + * @license gpl v. 3, see LICENSE.md for details. + */ + +#define CENTER_X 4 +#define CENTER_Y 4 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "satellite.h" +#include "ip65.h" + +const unsigned char _satellite[8] = { + 0x20, /* ..X.|.... */ + 0x50, /* .X.X|.... */ + 0xA4, /* X.X.|.X.. */ + 0x58, /* .X.X|X... */ + 0x1A, /* ...X|X.X. */ + 0x05, /* ....|.X.X */ + 0x0A, /* ....|X.X. */ + 0x04, /* ....|.X.. */ +}; + +// TODO: These tables are broken, recalculate them! + +unsigned char xpos[360] = + { 0,0,1,2,3,3,4,5,6,7,7,8,9,10,10,11,12,13,14,14,15,16,17,17,18,19,20,21,21,22,23,24,24,25,26,27,28,28,29,30,31,31,32,33,34,35,35,36,37,38,38,39,40,41,42,42,43,44,45,45,46,47,48,49,49,50,51,52,52,53,54,55,56,56,57,58,59,59,60,61,62,63,63,64,65,66,66,67,68,69,70,70,71,72,73,73,74,75,76,77,77,78,79,80,80,81,82,83,84,84,85,86,87,87,88,89,90,91,91,92,93,94,94,95,96,97,98,98,99,100,101,101,102,103,104,105,105,106,107,108,108,109,110,111,112,112,113,114,115,115,116,117,118,119,119,120,121,122,122,123,124,125,126,126,127,128,129,129,130,131,132,133,133,134,135,136,136,137,138,139,140,140,141,142,143,143,144,145,146,147,147,148,149,150,150,151,152,153,154,154,155,156,157,157,158,159,160,161,161,162,163,164,164,165,166,167,168,168,169,170,171,171,172,173,174,175,175,176,177,178,178,179,180,181,182,182,183,184,185,185,186,187,188,189,189,190,191,192,192,193,194,195,196,196,197,198,199,199,200,201,202,203,203,204,205,206,206,207,208,209,210,210,211,212,213,213,214,215,216,217,217,218,219,220,220,221,222,223,224,224,225,226,227,227,228,229,230,231,231,232,233,234,234,235,236,237,238,238,239,240,241,241,242,243,244,245,245,246,247,248,248,249,250,251,252,252,253,254,255,255,0,1,2,3,3,4,5,6,6,7,8,9,10,10,11,12,13,13,14,15,16,17,17,18,19,20,20,21,22,23, }; + +unsigned char ypos[360] = + { 0,0,0,1,1,2,2,3,3,4,4,4,5,5,6,6,7,7,8,8,8,9,9,10,10,11,11,12,12,12,13,13,14,14,15,15,16,16,16,17,17,18,18,19,19,20,20,20,21,21,22,22,23,23,24,24,24,25,25,26,26,27,27,28,28,28,29,29,30,30,31,31,32,32,32,33,33,34,34,35,35,36,36,36,37,37,38,38,39,39,40,40,40,41,41,42,42,43,43,44,44,44,45,45,46,46,47,47,48,48,48,49,49,50,50,51,51,52,52,52,53,53,54,54,55,55,56,56,56,57,57,58,58,59,59,60,60,60,61,61,62,62,63,63,64,64,64,65,65,66,66,67,67,68,68,68,69,69,70,70,71,71,72,72,72,73,73,74,74,75,75,76,76,76,77,77,78,78,79,79,80,80,80,81,81,82,82,83,83,84,84,84,85,85,86,86,87,87,88,88,88,89,89,90,90,91,91,92,92,92,93,93,94,94,95,95,96,96,96,97,97,98,98,99,99,100,100,100,101,101,102,102,103,103,104,104,104,105,105,106,106,107,107,108,108,108,109,109,110,110,111,111,112,112,112,113,113,114,114,115,115,116,116,116,117,117,118,118,119,119,120,120,120,121,121,122,122,123,123,124,124,124,125,125,126,126,127,127,128,128,128,129,129,130,130,131,131,132,132,132,133,133,134,134,135,135,136,136,136,137,137,138,138,139,139,140,140,140,141,141,142,142,143,143,144,144,144,145,145,146,146,147,147,148,148,148,149,149,150,150,151,151,152,152,152,153,153,154,154,155,155,156,156,156,157,157,158,158,159,159, }; + +const char url[]="HTTP://api.open-notify.org/iss-now.json"; +const char fmt[]="{\"iss_position\": {\"latitude\": \"%s \"longitude\": \"%s \"timestamp\": %ld, \"message\": \"success\"}"; + +char download[1024]; + +int post_proc(char *s) +{ + char *c = strchr(s,'"'); + *c = 0; + return atoi(s); +} + +bool satellite_fetch(int *lon, int *lat, char *lon_s, char *lat_s, unsigned long *ts) +{ + char *bdy; + + memset(lon_s,0,16); + memset(lat_s,0,16); + + if (!url_download(url,(uint8_t*)download,sizeof(download))) + return false; + + bdy=strstr(download,"\r\n\r\n"); + if (!bdy) + return false; + bdy += 4; + + if (sscanf(bdy,fmt,lon_s,lat_s,ts)!=3) + return false; + + *lon = post_proc(lon_s); + *lat = post_proc(lat_s); + + return true; +} + +void satellite(int lon, int lat) +{ + unsigned char x = xpos[lon + 180] - CENTER_X; + unsigned char y = ypos[lat + 180] - CENTER_Y; + unsigned char i,j; + int8_t b; + + for (i=0;i<8;i++) + { + b=_satellite[i]; + for (j=0;j<16;j+=2) + { + if (b < 0) + tgi_setcolor(TGI_COLOR_WHITE2); + else + tgi_setcolor(TGI_COLOR_BLACK2); + tgi_setpixel(x+j,y+i); + tgi_setpixel(x+j+1,y+i); + b <<= 1; + } + } +} diff --git a/examples/20-multiapp-arduino/iss-tracker/apple2/src/apple2.ip65/satellite.h b/examples/20-multiapp-arduino/iss-tracker/apple2/src/apple2.ip65/satellite.h new file mode 100644 index 0000000..c8293ea --- /dev/null +++ b/examples/20-multiapp-arduino/iss-tracker/apple2/src/apple2.ip65/satellite.h @@ -0,0 +1,15 @@ +/** + * ISS Tracker + * + * @author Thomas Cherryhomes + * @email thom dot cherryhomes at gmail dot com + * @license gpl v. 3, see LICENSE.md for details. + */ + +#ifndef SATELLITE_H +#define SATELLITE_H + +void satellite(int lon, int lat); +bool satellite_fetch(int *lon, int *lat, char *lon_s, char *lat_s, unsigned long *ts); + +#endif /* SATELLITE_H */ diff --git a/examples/20-multiapp-arduino/iss-tracker/apple2/src/apple2.ip65/sp.c b/examples/20-multiapp-arduino/iss-tracker/apple2/src/apple2.ip65/sp.c new file mode 100644 index 0000000..e69de29 diff --git a/examples/20-multiapp-arduino/iss-tracker/apple2/src/apple2idiot.c b/examples/20-multiapp-arduino/iss-tracker/apple2/src/apple2idiot.c new file mode 100644 index 0000000..67c76c4 --- /dev/null +++ b/examples/20-multiapp-arduino/iss-tracker/apple2/src/apple2idiot.c @@ -0,0 +1,87 @@ +#include +#include +#include +#include + +//#include "globals.h" +#include "apple2idiot.h" +//#include "../../../../arduino-lib/Apple2Idiot/A2I_commands.h" + +#define MAX_STR_LEN 250 + +#define CARD_ADDRESS 0xC200 +#define APPLE_COMMAND_ADDRESS 0xC201 +#define RAM_DATA_START_ADDRESS 0xC202 +#define ESP_TIMEOUT 5555 +#define ACK 6 // Acknowledge +#define APP_ID = 202 + +unsigned int read_byte(unsigned int address) { + unsigned int b = 0; + b = PEEK(address); + //printf("read_data(%u)", address); + return b; +} + +unsigned char write_byte(unsigned int address, unsigned char byte_to_write) { + //printf("%u <- %d, [%c]\n", address, byte_to_write, byte_to_write); + POKE(address, byte_to_write); +} + +unsigned char write_byte_wait_for_ack(unsigned int address, unsigned char byte_to_write) { + unsigned char received_esp_response = 0; + int timeout_count = 0; + unsigned char timeout_happened = 0; + int delay_count = 0; + unsigned char read_char; + write_byte(address, byte_to_write); + while ((received_esp_response==0) || (timeout_happened==0)) { + timeout_count++; + if (timeout_count > ESP_TIMEOUT) { + timeout_happened = 1; + return 0; + } + //read_char = read_byte(ESP_COMMAND_ADDRESS); + read_char = read_byte(APPLE_COMMAND_ADDRESS); + if (read_char == ACK) { + received_esp_response = 1; + return 1; + } + for (delay_count=0; delay_count < 1111; ++delay_count) { + // do nothing + } + } +} + +unsigned char* write_string_to_ram(unsigned int address, char* string_to_send) { + unsigned char i; + unsigned char size = strlen(string_to_send); + //gotoxy(0,2); + if (string_to_send[size-1] == '\n') { + string_to_send[size-1] = '\0'; + } + //printf("%u (%s)\n", address, string_to_send); + for (i=0; i +#include +#include +#include +#include "map.h" +#include "satellite.h" +#include "osd.h" +//#include "sp.h" +#include "apple2idiot.h" + +#define CARD_ADDRESS 0xC200 +#define APPLE_COMMAND_ADDRESS 0xC201 +#define RAM_DATA_START_ADDRESS 0xC202 +#define ESP_TIMEOUT 5555 +#define ACK 6 // Acknowledge +#define APP_ID = 202 + +//unsigned char net; +char lon_s[16], lat_s[16]; +int lat, lon; +long timer; +unsigned long ts; + +void main(void) +{ + //sp_init(); + //net = sp_find_network(); + tgi_install(tgi_static_stddrv); + tgi_init(); + tgi_apple2_mix(true); + tgi_clear(); + + write_byte(APPLE_COMMAND_ADDRESS, 202); + + while (1) + { + timer=524088; + clrscr(); + satellite_fetch(&lon,&lat,&lon_s,&lat_s,&ts); + map(); + osd(lon_s,lat_s,ts); + satellite(lon,lat); + + while (timer>0) + { + if (kbhit()) + switch(cgetc()) + { + case 0x1b: + return; + case 0x0D: + case 0x0A: + timer=0; + break; + default: + break; + } + + timer--; + } + } +} diff --git a/examples/20-multiapp-arduino/iss-tracker/apple2/src/map.c b/examples/20-multiapp-arduino/iss-tracker/apple2/src/map.c new file mode 100644 index 0000000..a677cea --- /dev/null +++ b/examples/20-multiapp-arduino/iss-tracker/apple2/src/map.c @@ -0,0 +1,31 @@ +/** + * ISS Tracker + * + * @author Thomas Cherryhomes + * @email thom dot cherryhomes at gmail dot com + * @license gpl v. 3, see LICENSE.md for details. + */ +#include +#include <6502.h> +#include +#include +#include +#include +#include +#include +#include + +void map(void) +{ + int f = open("MAP.HGR",O_RDONLY); + + if (f==-1) + { + perror("map open"); + cgetc(); + exit(1); + } + + read(f,(void *)0x2000,0x2000); + close(f); +} diff --git a/examples/20-multiapp-arduino/iss-tracker/apple2/src/map.h b/examples/20-multiapp-arduino/iss-tracker/apple2/src/map.h new file mode 100644 index 0000000..d176ef9 --- /dev/null +++ b/examples/20-multiapp-arduino/iss-tracker/apple2/src/map.h @@ -0,0 +1,14 @@ +/** + * ISS Tracker + * + * @author Thomas Cherryhomes + * @email thom dot cherryhomes at gmail dot com + * @license gpl v. 3, see LICENSE.md for details. + */ + +#ifndef MAP_H +#define MAP_H + +void map(void); + +#endif /* MAP_H */ diff --git a/examples/20-multiapp-arduino/iss-tracker/apple2/src/osd.c b/examples/20-multiapp-arduino/iss-tracker/apple2/src/osd.c new file mode 100644 index 0000000..32d1c62 --- /dev/null +++ b/examples/20-multiapp-arduino/iss-tracker/apple2/src/osd.c @@ -0,0 +1,18 @@ +/** + * ISS Tracker + * + * @author Thomas Cherryhomes + * @email thom dot cherryhomes at gmail dot com + * @license gpl v. 3, see LICENSE.md for details. + */ + +#include +#include +#include + +void osd(char *lon, char *lat,unsigned long ts) +{ + cprintf(" ** CURRENT ISS POSITION **\r\n"); + cprintf(" LON: %-14s LAT: %-14s",lon,lat); + cprintf(" AS OF: %s",ctime(&ts)); +} diff --git a/examples/20-multiapp-arduino/iss-tracker/apple2/src/osd.h b/examples/20-multiapp-arduino/iss-tracker/apple2/src/osd.h new file mode 100644 index 0000000..183d57c --- /dev/null +++ b/examples/20-multiapp-arduino/iss-tracker/apple2/src/osd.h @@ -0,0 +1,14 @@ +/** + * ISS Tracker + * + * @author Thomas Cherryhomes + * @email thom dot cherryhomes at gmail dot com + * @license gpl v. 3, see LICENSE.md for details. + */ + +#ifndef OSD_H +#define OSD_H + +void osd(char *lon, char *lat, unsigned long ts); + +#endif /* OSD_H */ diff --git a/examples/20-multiapp-arduino/iss-tracker/apple2/src/satellite.c b/examples/20-multiapp-arduino/iss-tracker/apple2/src/satellite.c new file mode 100644 index 0000000..28f086e --- /dev/null +++ b/examples/20-multiapp-arduino/iss-tracker/apple2/src/satellite.c @@ -0,0 +1,116 @@ +/** + * ISS Tracker + * + * @author Thomas Cherryhomes + * @email thom dot cherryhomes at gmail dot com + * @license gpl v. 3, see LICENSE.md for details. + */ + +#define CENTER_X 4 +#define CENTER_Y 4 + +#define APPLE_COMMAND_ADDRESS 0xC201 +#define RAM_DATA_START_ADDRESS 0xC202 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "satellite.h" +#include "apple2idiot.h" +//#include "sp.h" + +//extern unsigned char net; // Network device as found by sp_find_network() + +const unsigned char _satellite[8] = { + 0x20, /* ..X.|.... */ + 0x50, /* .X.X|.... */ + 0xA4, /* X.X.|.X.. */ + 0x58, /* .X.X|X... */ + 0x1A, /* ...X|X.X. */ + 0x05, /* ....|.X.X */ + 0x0A, /* ....|X.X. */ + 0x04, /* ....|.X.. */ +}; + +// TODO: These tables are broken, recalculate them! + +unsigned char xpos[360] = + { 0,0,1,2,3,3,4,5,6,7,7,8,9,10,10,11,12,13,14,14,15,16,17,17,18,19,20,21,21,22,23,24,24,25,26,27,28,28,29,30,31,31,32,33,34,35,35,36,37,38,38,39,40,41,42,42,43,44,45,45,46,47,48,49,49,50,51,52,52,53,54,55,56,56,57,58,59,59,60,61,62,63,63,64,65,66,66,67,68,69,70,70,71,72,73,73,74,75,76,77,77,78,79,80,80,81,82,83,84,84,85,86,87,87,88,89,90,91,91,92,93,94,94,95,96,97,98,98,99,100,101,101,102,103,104,105,105,106,107,108,108,109,110,111,112,112,113,114,115,115,116,117,118,119,119,120,121,122,122,123,124,125,126,126,127,128,129,129,130,131,132,133,133,134,135,136,136,137,138,139,140,140,141,142,143,143,144,145,146,147,147,148,149,150,150,151,152,153,154,154,155,156,157,157,158,159,160,161,161,162,163,164,164,165,166,167,168,168,169,170,171,171,172,173,174,175,175,176,177,178,178,179,180,181,182,182,183,184,185,185,186,187,188,189,189,190,191,192,192,193,194,195,196,196,197,198,199,199,200,201,202,203,203,204,205,206,206,207,208,209,210,210,211,212,213,213,214,215,216,217,217,218,219,220,220,221,222,223,224,224,225,226,227,227,228,229,230,231,231,232,233,234,234,235,236,237,238,238,239,240,241,241,242,243,244,245,245,246,247,248,248,249,250,251,252,252,253,254,255,255,0,1,2,3,3,4,5,6,6,7,8,9,10,10,11,12,13,13,14,15,16,17,17,18,19,20,20,21,22,23, }; + +unsigned char ypos[360] = + { 0,0,0,1,1,2,2,3,3,4,4,4,5,5,6,6,7,7,8,8,8,9,9,10,10,11,11,12,12,12,13,13,14,14,15,15,16,16,16,17,17,18,18,19,19,20,20,20,21,21,22,22,23,23,24,24,24,25,25,26,26,27,27,28,28,28,29,29,30,30,31,31,32,32,32,33,33,34,34,35,35,36,36,36,37,37,38,38,39,39,40,40,40,41,41,42,42,43,43,44,44,44,45,45,46,46,47,47,48,48,48,49,49,50,50,51,51,52,52,52,53,53,54,54,55,55,56,56,56,57,57,58,58,59,59,60,60,60,61,61,62,62,63,63,64,64,64,65,65,66,66,67,67,68,68,68,69,69,70,70,71,71,72,72,72,73,73,74,74,75,75,76,76,76,77,77,78,78,79,79,80,80,80,81,81,82,82,83,83,84,84,84,85,85,86,86,87,87,88,88,88,89,89,90,90,91,91,92,92,92,93,93,94,94,95,95,96,96,96,97,97,98,98,99,99,100,100,100,101,101,102,102,103,103,104,104,104,105,105,106,106,107,107,108,108,108,109,109,110,110,111,111,112,112,112,113,113,114,114,115,115,116,116,116,117,117,118,118,119,119,120,120,120,121,121,122,122,123,123,124,124,124,125,125,126,126,127,127,128,128,128,129,129,130,130,131,131,132,132,132,133,133,134,134,135,135,136,136,136,137,137,138,138,139,139,140,140,140,141,141,142,142,143,143,144,144,144,145,145,146,146,147,147,148,148,148,149,149,150,150,151,151,152,152,152,153,153,154,154,155,155,156,156,156,157,157,158,158,159,159, }; + +const char url[]="N:HTTP://api.open-notify.org/iss-now.json"; +const char longitude_query[]="/iss_position/longitude"; +const char latitude_query[]="/iss_position/latitude"; +const char timestamp_query[]="/timestamp"; + +int post_proc(char *s) +{ + char *c = strchr(s,'"'); + *c = 0; + return atoi(s); +} + +bool satellite_fetch(int *lon, int *lat, char *lon_s, char *lat_s, unsigned long *ts) +{ + //unsigned short len; + char ts_s[16]; + unsigned int address_offset = 0; + + //gotoxy(0,0); + + memset(lon_s,0,16); + memset(lat_s,0,16); + + write_byte_wait_for_ack(APPLE_COMMAND_ADDRESS, 20); + + address_offset = 0; + read_string_from_ram(RAM_DATA_START_ADDRESS + address_offset, lat_s, 9); + address_offset += strlen(lat_s) + 1; + read_string_from_ram(RAM_DATA_START_ADDRESS + address_offset, lon_s, 9); + address_offset += strlen(lon_s) + 1; + read_string_from_ram(RAM_DATA_START_ADDRESS + address_offset, ts_s, 9); + + *ts=atol(ts_s); + + //clrscr(); + //cprintf(" ** CURRENT ISS POSITION **\r\n"); + //cprintf(" LON: %-14s LAT: %-14s",lon_s,lat_s); + //cprintf(" TS: %-14s",ts_s); + //cprintf(" AS OF: %s",ctime(ts)); + + *lon=atoi(lon_s); + *lat=atoi(lat_s); + *ts=atol(ts_s); + + return true; // todo come back here and add error handling. +} + +void satellite(int lon, int lat) +{ + unsigned char x = xpos[lon + 180] - CENTER_X; + unsigned char y = ypos[lat + 180] - CENTER_Y; + unsigned char i,j; + int8_t b; + for (i=0;i<8;i++) + { + b=_satellite[i]; + for (j=0;j<16;j+=2) + { + if (b < 0) + tgi_setcolor(TGI_COLOR_WHITE2); + else + tgi_setcolor(TGI_COLOR_BLACK2); + tgi_setpixel(x+j,y+i); + tgi_setpixel(x+j+1,y+i); + b <<= 1; + } + } +} diff --git a/examples/20-multiapp-arduino/iss-tracker/apple2/src/satellite.h b/examples/20-multiapp-arduino/iss-tracker/apple2/src/satellite.h new file mode 100644 index 0000000..c8293ea --- /dev/null +++ b/examples/20-multiapp-arduino/iss-tracker/apple2/src/satellite.h @@ -0,0 +1,15 @@ +/** + * ISS Tracker + * + * @author Thomas Cherryhomes + * @email thom dot cherryhomes at gmail dot com + * @license gpl v. 3, see LICENSE.md for details. + */ + +#ifndef SATELLITE_H +#define SATELLITE_H + +void satellite(int lon, int lat); +bool satellite_fetch(int *lon, int *lat, char *lon_s, char *lat_s, unsigned long *ts); + +#endif /* SATELLITE_H */ diff --git a/examples/20-multiapp-arduino/iss-tracker/apple2/src/sp.c b/examples/20-multiapp-arduino/iss-tracker/apple2/src/sp.c new file mode 100644 index 0000000..7878881 --- /dev/null +++ b/examples/20-multiapp-arduino/iss-tracker/apple2/src/sp.c @@ -0,0 +1,365 @@ +#ifdef BUILD_APPLE2 +/** + * FujiNet CONFIG for #Apple2 + * + * SmartPort MLI Routines + */ + +#ifdef __INTELLISENSE__ +// 18, expect closing parenthses - needed to use cc65 inline asm command with agruments. + #pragma diag_suppress 18 +#endif + + +#include "sp.h" +#include +#include +#include +#include + +#define SP_CMD_STATUS 0 +#define SP_CMD_CONTROL 4 +#define SP_CMD_OPEN 6 +#define SP_CMD_CLOSE 7 +#define SP_CMD_READ 8 +#define SP_CMD_WRITE 9 +#define SP_STATUS_PARAM_COUNT 3 +#define SP_CONTROL_PARAM_COUNT 3 +#define SP_OPEN_PARAM_COUNT 1 +#define SP_CLOSE_PARAM_COUNT 1 +#define SP_READ_PARAM_COUNT 4 +#define SP_WRITE_PARAM_COUNT 4 + +// extern globals: +uint8_t sp_payload[1024]; +uint16_t sp_count; +uint8_t sp_dest; +uint16_t sp_dispatch; +uint8_t sp_error; + +static uint8_t sp_cmdlist[10]; +static uint8_t sp_cmdlist_low, sp_cmdlist_high; +static uint8_t sp_err, sp_rtn_low, sp_rtn_high; + +int8_t sp_status(uint8_t dest, uint8_t statcode) +{ + sp_error = 0; + // build the command list + sp_cmdlist[0] = SP_STATUS_PARAM_COUNT; + sp_cmdlist[1] = dest; // set before calling sp_status(); + sp_cmdlist[2] = (uint8_t)((uint16_t)&sp_payload & 0x00FF); + sp_cmdlist[3] = (uint8_t)((uint16_t)&sp_payload >> 8) & 0xFF; + sp_cmdlist[4] = statcode; + + sp_cmdlist_low = (uint8_t)((uint16_t)&sp_cmdlist & 0x00FF); + sp_cmdlist_high = (uint8_t)((uint16_t)&sp_cmdlist >> 8) & 0xFF; + + + // store cmd list + __asm__ volatile ("lda #%b", SP_CMD_STATUS); + __asm__ volatile ("sta %g", spCmd); // store status command # + __asm__ volatile ("lda %v", sp_cmdlist_low); + __asm__ volatile ("sta %g", spCmdListLow); // store status command # + __asm__ volatile ("lda %v", sp_cmdlist_high); + __asm__ volatile ("sta %g", spCmdListHigh); // store status command # + + __asm__ volatile ("jsr $C50D"); // to do - find SP entry point using algorithm from firmware reference +spCmd: + __asm__ volatile ("nop"); +spCmdListLow: + __asm__ volatile ("nop"); +spCmdListHigh: + __asm__ volatile ("nop"); + __asm__ volatile ("stx %v", sp_rtn_low); + __asm__ volatile ("sty %v", sp_rtn_high); + __asm__ volatile ("sta %v", sp_err); + + sp_count = ((uint16_t)sp_rtn_high << 8) | (uint16_t)sp_rtn_low; + sp_error = sp_err; + return sp_err; +} + +int8_t sp_control(uint8_t dest, uint8_t ctrlcode) +{ + sp_error = 0; + // sp_dest = 5; // need to search + // build the command list + sp_cmdlist[0] = SP_CONTROL_PARAM_COUNT; + sp_cmdlist[1] = dest; // set before calling sp_status(); + sp_cmdlist[2] = (uint8_t)((uint16_t)&sp_payload & 0x00FF); + sp_cmdlist[3] = (uint8_t)((uint16_t)&sp_payload >> 8) & 0xFF; + sp_cmdlist[4] = ctrlcode; + + sp_cmdlist_low = (uint8_t)((uint16_t)&sp_cmdlist & 0x00FF); + sp_cmdlist_high = (uint8_t)((uint16_t)&sp_cmdlist >> 8) & 0xFF; + + // store cmd list + __asm__ volatile ("lda #%b", SP_CMD_CONTROL); + __asm__ volatile ("sta %g", spCmd); // store status command # + __asm__ volatile ("lda %v", sp_cmdlist_low); + __asm__ volatile ("sta %g", spCmdListLow); // store status command # + __asm__ volatile ("lda %v", sp_cmdlist_high); + __asm__ volatile ("sta %g", spCmdListHigh); // store status command # + + __asm__ volatile ("jsr $C50D"); // to do - find entry point and used it instead of hardcoded address +spCmd: + __asm__ volatile ("nop"); +spCmdListLow: + __asm__ volatile ("nop"); +spCmdListHigh: + __asm__ volatile ("nop"); + __asm__ volatile ("sta %v", sp_err); + sp_error = sp_err; + return sp_err; +} + +int8_t sp_open(uint8_t dest) +{ + sp_error = 0; + // sp_dest = 5; // need to search + // build the command list + sp_cmdlist[0] = SP_OPEN_PARAM_COUNT; + sp_cmdlist[1] = dest; // set before calling sp_status(); + + sp_cmdlist_low = (uint8_t)((uint16_t)&sp_cmdlist & 0x00FF); + sp_cmdlist_high = (uint8_t)((uint16_t)&sp_cmdlist >> 8) & 0xFF; + + // store cmd list + __asm__ volatile ("lda #%b", SP_CMD_OPEN); + __asm__ volatile ("sta %g", spCmd); // store status command # + __asm__ volatile ("lda %v", sp_cmdlist_low); + __asm__ volatile ("sta %g", spCmdListLow); // store status command # + __asm__ volatile ("lda %v", sp_cmdlist_high); + __asm__ volatile ("sta %g", spCmdListHigh); // store status command # + + __asm__ volatile ("jsr $C50D"); // to do - find entry point and used it instead of hardcoded address +spCmd: + __asm__ volatile ("nop"); +spCmdListLow: + __asm__ volatile ("nop"); +spCmdListHigh: + __asm__ volatile ("nop"); + __asm__ volatile ("sta %v", sp_err); + sp_error = sp_err; + return sp_err; +} + +int8_t sp_close(uint8_t dest) +{ + sp_error = 0; + // sp_dest = 5; // need to search + // build the command list + sp_cmdlist[0] = SP_CLOSE_PARAM_COUNT; + sp_cmdlist[1] = dest; // set before calling sp_status(); + + sp_cmdlist_low = (uint8_t)((uint16_t)&sp_cmdlist & 0x00FF); + sp_cmdlist_high = (uint8_t)((uint16_t)&sp_cmdlist >> 8) & 0xFF; + + // store cmd list + __asm__ volatile ("lda #%b", SP_CMD_CLOSE); + __asm__ volatile ("sta %g", spCmd); // store status command # + __asm__ volatile ("lda %v", sp_cmdlist_low); + __asm__ volatile ("sta %g", spCmdListLow); // store status command # + __asm__ volatile ("lda %v", sp_cmdlist_high); + __asm__ volatile ("sta %g", spCmdListHigh); // store status command # + + __asm__ volatile ("jsr $C50D"); // to do - find entry point and used it instead of hardcoded address +spCmd: + __asm__ volatile ("nop"); +spCmdListLow: + __asm__ volatile ("nop"); +spCmdListHigh: + __asm__ volatile ("nop"); + __asm__ volatile ("sta %v", sp_err); + sp_error = sp_err; + return sp_err; +} + +int8_t sp_read(uint8_t dest, uint16_t len) +{ + sp_error = 0; + // sp_dest = 5; // need to search + // build the command list + sp_cmdlist[0] = SP_READ_PARAM_COUNT; + sp_cmdlist[1] = dest; // set before calling sp_status(); + sp_cmdlist[2] = (uint8_t)((uint16_t)&sp_payload & 0x00FF); + sp_cmdlist[3] = (uint8_t)((uint16_t)&sp_payload >> 8) & 0xFF; + sp_cmdlist[4] = len & 0xFF; + sp_cmdlist[5] = len >> 8; + + sp_cmdlist_low = (uint8_t)((uint16_t)&sp_cmdlist & 0x00FF); + sp_cmdlist_high = (uint8_t)((uint16_t)&sp_cmdlist >> 8) & 0xFF; + + // store cmd list + __asm__ volatile ("lda #%b", SP_CMD_READ); + __asm__ volatile ("sta %g", spCmd); // store status command # + __asm__ volatile ("lda %v", sp_cmdlist_low); + __asm__ volatile ("sta %g", spCmdListLow); // store status command # + __asm__ volatile ("lda %v", sp_cmdlist_high); + __asm__ volatile ("sta %g", spCmdListHigh); // store status command # + + __asm__ volatile ("jsr $C50D"); // to do - find entry point and used it instead of hardcoded address +spCmd: + __asm__ volatile ("nop"); +spCmdListLow: + __asm__ volatile ("nop"); +spCmdListHigh: + __asm__ volatile ("nop"); + __asm__ volatile ("sta %v", sp_err); + sp_error = sp_err; + return sp_err; +} + +int8_t sp_write(uint8_t dest, uint16_t len) +{ + sp_error = 0; + // build the command list + sp_cmdlist[0] = SP_READ_PARAM_COUNT; + sp_cmdlist[1] = dest; // set before calling sp_status(); + sp_cmdlist[2] = (uint8_t)((uint16_t)&sp_payload & 0x00FF); + sp_cmdlist[3] = (uint8_t)((uint16_t)&sp_payload >> 8) & 0xFF; + sp_cmdlist[4] = len & 0xFF; + sp_cmdlist[5] = len >> 8; + + sp_cmdlist_low = (uint8_t)((uint16_t)&sp_cmdlist & 0x00FF); + sp_cmdlist_high = (uint8_t)((uint16_t)&sp_cmdlist >> 8) & 0xFF; + + // store cmd list + __asm__ volatile ("lda #%b", SP_CMD_WRITE); + __asm__ volatile ("sta %g", spCmd); // store status command # + __asm__ volatile ("lda %v", sp_cmdlist_low); + __asm__ volatile ("sta %g", spCmdListLow); // store status command # + __asm__ volatile ("lda %v", sp_cmdlist_high); + __asm__ volatile ("sta %g", spCmdListHigh); // store status command # + + __asm__ volatile ("jsr $C50D"); // to do - find entry point and used it instead of hardcoded address +spCmd: + __asm__ volatile ("nop"); +spCmdListLow: + __asm__ volatile ("nop"); +spCmdListHigh: + __asm__ volatile ("nop"); + __asm__ volatile ("sta %v", sp_err); + sp_error = sp_err; + return sp_err; +} + +int8_t sp_find_fuji() +{ + // const char fuji[9] = "THE_FUJI"; + const char fuji[14] = "FUJINET_DISK_0"; + const uint8_t fuji_len = sizeof(fuji); + int8_t err, num, i, j; + + err = sp_status(0x00, 0x00); // get number of devices + if (err) + return -err; + num = sp_payload[0]; + num++; + for (i = 1; i < num; i++) + { + //do + err = sp_status(i, 0x03); // get DIB + //while (err); + if (sp_payload[4] == fuji_len) + { + for (j = 0; j < fuji_len; j++) + if (fuji[j]!=sp_payload[5+j]) + return 0; + sp_dest = i; // store the fuji unit # + return i; + } + } + return 0; +} + +int8_t sp_find_network() +{ + const char net[7] = "NETWORK"; + const uint8_t net_len = sizeof(net); + int8_t err, num, i, j; + + err = sp_status(0x00, 0x00); // get number of devices + + if (err) + return -err; + + num = sp_payload[0]; + num+=2; + + for (i = 1; i < num; i++) + { + err = sp_status(i, 0x03); // get DIB + + if (sp_payload[4] == net_len) + { + for (j = 0; j < net_len; j++) + if (net[j]!=sp_payload[5+j]) + return 0; + + return i; + } + } + printf("NET NOT FOUND"); + cgetc(); + return 0; +} + +/** + * Apple // SmartPort routines for CC65 + * + * @author Thomas Cherryhomes + * @email thom dot cherryhomes at gmail dot com + * + */ + +/** + * Check for SmartPort presence + * @return slot #, or 0xFF if not present. + */ +uint8_t sp_find_slot(void) +{ + uint8_t s=0; + + for (s=7; s-- > 0;) + { + uint16_t a = 0xc000 + (s * 0x100); + if ((PEEK(a+1) == 0x20) && + (PEEK(a+3) == 0x00) && + (PEEK(a+5) == 0x03) && + (PEEK(a+7) == 0x00)) + return s; + } + + // Did not find. + return 0; +} + +/** + * Return dispatch address for Smartport slot. + * @param s Slot # (1-7) + * @return smartport dispatch address + */ +uint16_t sp_dispatch_address(uint8_t slot) +{ + uint16_t a = (slot * 0x100) + 0xC000; + uint8_t j = PEEK(a+0xFF); + + return a + j + 3; +} + +void sp_init(void) +{ + uint8_t slot, f; + slot = sp_find_slot(); + if (slot) + sp_dispatch = sp_dispatch_address(slot); + else + cprintf("No SmartPort Firmware Found!"); + //sp_list_devs(); + f = sp_find_fuji(); + if (f < 1) + cprintf("FujiNet Not Found!"); +} + +#endif /* BUILD_APPLE2 */ diff --git a/examples/20-multiapp-arduino/iss-tracker/apple2/src/sp.h b/examples/20-multiapp-arduino/iss-tracker/apple2/src/sp.h new file mode 100644 index 0000000..7540771 --- /dev/null +++ b/examples/20-multiapp-arduino/iss-tracker/apple2/src/sp.h @@ -0,0 +1,30 @@ +/** + * FujiNet CONFIG for #Apple2 + * + * SmartPort MLI Routines + */ + +#ifndef SP_H +#define SP_H + +#include + +extern uint8_t sp_payload[1024]; +extern uint16_t sp_count, sp_dispatch; +extern uint8_t sp_dest; +extern uint8_t sp_error; + +int8_t sp_status(uint8_t dest, uint8_t statcode); +int8_t sp_control(uint8_t dest, uint8_t ctrlcode); +int8_t sp_open(uint8_t dest); +int8_t sp_close(uint8_t dest); +int8_t sp_read(uint8_t dest, uint16_t len); +int8_t sp_write(uint8_t dest, uint16_t len); +int8_t sp_find_fuji(void); +int8_t sp_find_network(void); +uint8_t sp_find_slot(void); +uint16_t sp_dispatch_address(uint8_t slot); +void sp_init(void); + +#endif /* SP_H */ + diff --git a/examples/30-dnd5eapi-cc65/30-dnd5eapi-cc65.ino b/examples/30-dnd5eapi-cc65/30-dnd5eapi-cc65.ino new file mode 100644 index 0000000..5073521 --- /dev/null +++ b/examples/30-dnd5eapi-cc65/30-dnd5eapi-cc65.ino @@ -0,0 +1,154 @@ +/* +Nathanial Hendler +2021 +github.com/equant +*/ + +// Load Wi-Fi library +#include +#include +#include "credentials.h" +//#include "a2i_weather.h" +//#include "a2i_iss.h" +//#include "a2i_chess.h" +#include "a2i_dnd5eapi.h" + +#define AUTO_CONNECT_TO_WIFI 1 +#define AUTO_CONNECT_TIMEOUT 5 + +Apple2Idiot a2i = Apple2Idiot(); + +/*################################################ +# Applications we're going to support # +# +# An Apple ][ running this card may want to run +# several different "apps" that utilize the card. +# These "apps" are classes (instantiated below) +# which the main loop uses to handle/manage +# communication with the Apple. For example, the +# card may want to handle requests to support +# programs on the Apple ][ such as a wifi +# selector, a chess game, and a weather lookup +# app. +################################################*/ + +Dnd5eapi dnd5eapi_app = Dnd5eapi(); +//Weather weather_app = Weather(); +//Iss iss_app = Iss(); + +#define N_APPS 1 +//byte app_ids[N_APPS] = {APP_ISS, APP_WEATHER, APP_DND5EAPI}; +byte app_ids[N_APPS] = {APP_DND5EAPI}; + +/*******************/ +/* Variables */ +/*******************/ + +byte current_app_id; + +const long mainLoopInterval = 100; // millis +//const long mainLoopInterval = 10000; // millis +unsigned long lastMainLoopTime = 0; +byte lastAppleCommand = 0; + +/*################################################ +# Setup # +################################################*/ + +void setup() { + Serial.begin(115200); + + a2i.init(); + +#ifdef AUTO_CONNECT_TO_WIFI + Serial.println(""); + Serial.print("Starting wifi, connecting to: "); + Serial.println(WIFI_SSID); + WiFi.begin(WIFI_SSID, WIFI_PASSWORD); + byte wifi_loop_count = 0; + while (WiFi.status() != WL_CONNECTED) { + delay(600); + wifi_loop_count++; + Serial.print("."); + if (wifi_loop_count > AUTO_CONNECT_TIMEOUT) { + break; + } + } + if (WiFi.status() == WL_CONNECTED) { + Serial.println(""); + Serial.println("WiFi connected successfully"); + Serial.print("Got IP: "); + Serial.println(WiFi.localIP()); //Show ESP32 IP on serial + } else { + Serial.println(""); + Serial.println("WiFi connection failed."); + } +#else + // Set WiFi to station mode and disconnect from an AP if it was previously connected + Serial.println("Wifi autoconnect not enabled. No wifi connection attempted."); + WiFi.mode(WIFI_STA); + WiFi.disconnect(); +#endif + + Serial.println("Setup done"); + current_app_id = app_ids[0]; + +} + +/*################################################ +# Main # +################################################*/ + +void loop() { + + if ((millis() - lastMainLoopTime) > mainLoopInterval) { + //Serial.print("."); + byte command_byte = a2i.read_data(APPLE_COMMAND_ADDRESS); + if (command_byte == RAM_BUSY) { + Serial.println("Command Read: RAM BUSY"); + } + else if (command_byte != lastAppleCommand){ + lastAppleCommand = command_byte; + byte result = 0; + Serial.print("Handling command_byte: "); + Serial.println(command_byte); + + /* Do we need to switch app context? */ + for (int i=0; i +#include +#include +#include +#include +#include +#include "chess_commands.h" +#include "a2i_chess.h" + + +void Chess::init(Apple2Idiot *a2ip, HTTPClient *httpp) { + a2i = a2ip; + http = httpp; + //strcpy(game_string, "a2a3e7e5e2e4"); +} + +byte Chess::handleCommand(byte command) { + switch(command) { + case CHESS_MAKE_MOVE: { + a2i->write_data(ESP_COMMAND_ADDRESS, ACK); // notify Apple IIe we are processing command byte + Serial.println("CHESS() MAKE_MOVE"); + String move_string; + move_string = a2i->read_string_from_ram(SHARED_RAM_START_ADDRESS); + Serial.println("Received move: ["+move_string+"]"); + byte result = makeMove(move_string); + int address_counter = a2i->write_string_to_shared_ram(last_ai_move, SHARED_RAM_START_ADDRESS); + getBoard(); + for (int i=0; i<9; i++) { + address_counter = a2i->write_string_to_shared_ram(game_board[i], address_counter + 1); + } + a2i->write_data(ESP_COMMAND_ADDRESS, result); + a2i->read_ram(11); + return ACK; + } + case CHESS_NEW_GAME: { + a2i->write_data(ESP_COMMAND_ADDRESS, ACK); // notify Apple IIe we are processing command byte + Serial.println("CHESS_NEW_GAME"); + strcpy(game_string, ""); + return ACK; + } + default: { + return COMMAND_NOT_FOUND; + } + } +} + +byte Chess::makeMove(String move_string) { + /* Apple says MAKE_MOVE and sends "a2a3" + * + * The basic flow... + * + * validateMove() We check that it's valid. + * If it is we accept the move and add it to game_string + * getGameStatus() Then we check the status and return (did someone win or lose?) + * getAIMove() Then we get the AI move. + * Then we check the status and return (did someone win or lose?) + */ + Apple2Idiot a2i; + HTTPClient http; + if (validateMove(move_string) == CHESS_VALID_MOVE) { + strcat(game_string, move_string.c_str()); + } else { + return CHESS_INVALID_MOVE; + } + char* game_status; + //b = getGameStatus(game_string); + game_status = getGameStatus(game_string); + Serial.print("after player move game_status:"); Serial.println(game_status); + + if (strcmp(game_status, "in_progress") == 0) { + //char* ai_move; + //ai_move = getAIMove(); + //strcat(game_string, ai_move); + //last_ai_move = getAIMove(); + strcpy(last_ai_move, getAIMove()); + strcat(game_string, last_ai_move); + game_status = getGameStatus(game_string); + Serial.print("after AI move game_status:"); Serial.println(game_status); + if (strcmp(game_status, "in_progress") == 0) { return STATUS_IN_PROGRESS; } + else if (strcmp(game_status, "black_won") == 0) { return STATUS_BLACK_WON; } + else if (strcmp(game_status, "white_won") == 0) { return STATUS_WHITE_WON; } + else if (strcmp(game_status, "white_won_resign") == 0) { return STATUS_UNHANDLED; } + else if (strcmp(game_status, "black_won_resign") == 0) { return STATUS_UNHANDLED; } + else if (strcmp(game_status, "stalemate") == 0) { return STATUS_STALEMATE; } + else if (strcmp(game_status, "insufficient_material") == 0) { return STATUS_UNHANDLED; } + else if (strcmp(game_status, "fifty_rule_move") == 0) { return STATUS_FIFTY_RULE_MOVE; } + else if (strcmp(game_status, "threefold_repitition") == 0) { return STATUS_UNHANDLED; } + else if (strcmp(game_status, "unknown") == 0) { return STATUS_UNKNOWN; } else { return STATUS_ERROR; } + } + else if (strcmp(game_status, "black_won") == 0) { return STATUS_BLACK_WON; } + else if (strcmp(game_status, "white_won") == 0) { return STATUS_WHITE_WON; } + else if (strcmp(game_status, "white_won_resign") == 0) { return STATUS_UNHANDLED; } + else if (strcmp(game_status, "black_won_resign") == 0) { return STATUS_UNHANDLED; } + else if (strcmp(game_status, "stalemate") == 0) { return STATUS_STALEMATE; } + else if (strcmp(game_status, "insufficient_material") == 0) { return STATUS_UNHANDLED; } + else if (strcmp(game_status, "fifty_rule_move") == 0) { return STATUS_FIFTY_RULE_MOVE; } + else if (strcmp(game_status, "threefold_repitition") == 0) { return STATUS_UNHANDLED; } + else if (strcmp(game_status, "unknown") == 0) { return STATUS_UNKNOWN; } else { return STATUS_ERROR; } +} + +char* Chess::getGameStatus(char* game_string) { + Apple2Idiot a2i; + HTTPClient http; + Serial.print("getGameStatus() "); Serial.println(game_status); + + char api_request[MAX_STR_LEN]; + sprintf(api_request, "%s/status/%s", api_entry_point, game_string); + Serial.print(" "); Serial.println(api_request); + http.begin(api_request); + int httpCode = http.GET(); //Make the request + delay(10); + if (httpCode > 0) { //Check for the returning code + Serial.println(" Success on HTTP request"); + String payload = http.getString(); + //Serial.println("++++++++++++++++++++++++"); + //Serial.println(payload); + //Serial.println("++++++++++++++++++++++++"); + StaticJsonDocument<400> doc; + DeserializationError error = deserializeJson(doc, payload); + if (error) { + Serial.print(F(" deserializeJson() failed: ")); + Serial.println(error.f_str()); + return "error"; + } else { + Serial.println("----------------------"); + serializeJsonPretty(doc, Serial); + Serial.println(); + Serial.println("----------------------"); + //return doc["gameStatus"]; + return (char *)doc["gameStatus"].as(); + } + } else { + Serial.println("Error on HTTP request"); + return "error"; + } + // Don't know how we could get here without it being an error. + + return "error"; +} + +char* Chess::getAIMove() { + Apple2Idiot a2i; + HTTPClient http; + Serial.print("getAIMove() "); Serial.println(game_status); + + char api_request[MAX_STR_LEN]; + sprintf(api_request, "%s/next_best/%s", api_entry_point, game_string); + Serial.print(" "); Serial.println(api_request); + http.begin(api_request); + int httpCode = http.GET(); //Make the request + delay(10); + if (httpCode > 0) { //Check for the returning code + Serial.println(" Success on HTTP request"); + String payload = http.getString(); + //Serial.println("++++++++++++++++++++++++"); + //Serial.println(payload); + //Serial.println("++++++++++++++++++++++++"); + StaticJsonDocument<400> doc; + DeserializationError error = deserializeJson(doc, payload); + if (error) { + Serial.print(F(" deserializeJson() failed: ")); + Serial.println(error.f_str()); + return "jsonerror"; + } else { + Serial.println("----------------------"); + serializeJsonPretty(doc, Serial); + Serial.println(); + Serial.println("----------------------"); + //return doc["gameStatus"]; + return (char *)doc["bestNext"].as(); + } + } else { + Serial.println("Error on HTTP request"); + return "geterror"; + } + // Don't know how we could get here without it being an error. + + return "unknownerror"; +} + +byte Chess::validateMove(String move_string) { + Apple2Idiot a2i; + HTTPClient http; + Serial.print("validateMove() "); Serial.println(move_string); + char api_request[MAX_STR_LEN]; + sprintf(api_request, "%s/valid_move/%s%s", api_entry_point, game_string, move_string); + Serial.print(" "); Serial.println(api_request); + http.begin(api_request); + int httpCode = http.GET(); //Make the request + delay(10); + if (httpCode > 0) { //Check for the returning code + Serial.println(" Success on HTTP request"); + String payload = http.getString(); + //Serial.println("++++++++++++++++++++++++"); + //Serial.println(payload); + //Serial.println("++++++++++++++++++++++++"); + StaticJsonDocument<400> doc; + DeserializationError error = deserializeJson(doc, payload); + if (error) { + Serial.print(F("deserializeJson() failed: ")); + Serial.println(error.f_str()); + return ERR; + } else { + Serial.println("----------------------"); + serializeJsonPretty(doc, Serial); + Serial.println(); + Serial.println("----------------------"); + bool validMove = doc["validMove"]; + if (validMove) { + // good move + Serial.println(" Valid move!"); + return CHESS_VALID_MOVE; + } else { + Serial.println(" Invalid move!"); + return CHESS_INVALID_MOVE; + } + } + } else { + Serial.println("Error on HTTP request"); + return ERR; + } + // Don't know how we could get here without it being an error. + return ERR; +} + +void Chess::removeSubstr (char *string, char *sub) { + char *match = string; + int len = strlen(sub); + while ((match = strstr(match, sub))) { + *match = '\0'; + strcat(string, match+len); + match++; + } +} + +void Chess::getBoard() { + Apple2Idiot a2i; + HTTPClient http; + Serial.println("getBoard() "); + char api_request[MAX_STR_LEN]; + sprintf(api_request, "%s/board_string/%s", api_entry_point, game_string); + Serial.print(" "); Serial.println(api_request); + http.begin(api_request); + int httpCode = http.GET(); //Make the request + delay(10); + if (httpCode > 0) { //Check for the returning code + Serial.println(" Success on HTTP request"); + String payload = http.getString(); + //Serial.println("++++++++++++++++++++++++"); + //Serial.println(payload); + //Serial.println("++++++++++++++++++++++++"); + StaticJsonDocument<400> doc; + DeserializationError error = deserializeJson(doc, payload); + if (error) { + Serial.print(F("deserializeJson() failed: ")); + Serial.println(error.f_str()); + } else { + //Serial.println("----------------------"); + //serializeJsonPretty(doc, Serial); + //Serial.println(); + //Serial.println("----------------------"); + //Serial.println(" Break up the board..."); + //Serial.print(" BOARD:"); Serial.println((char *)doc["board"].as()); + char* pch = NULL; + pch = strtok((char *)doc["board"].as(), "\n"); + //Serial.print(" pch:");Serial.println(pch); + int row_count = 0; + while (pch != NULL) { + char board_line[30]; + strcpy(board_line, pch); + removeSubstr(board_line, "[37m"); + removeSubstr(board_line, "[0m"); + removeSubstr(board_line, "\n"); + removeSubstr(board_line, "\e"); // remove escape (ASCII 27) + removeSubstr(board_line, "\e"); // remove escape (ASCII 27) + //Serial.print("(");Serial.print(row_count);Serial.print(")"); + //Serial.print(board_line); Serial.println("|"); + strcpy(game_board[row_count], board_line); // valid + pch = strtok(NULL, "\n"); + row_count++; + } + Serial.println(); + for (int i=0; i<9; i++) { + Serial.print("[");Serial.print(game_board[i]);Serial.println("]"); + } + } + } else { + Serial.println("Error on HTTP request"); + } + // Don't know how we could get here without it being an error. +} diff --git a/examples/template/a2i_chess.h b/examples/30-dnd5eapi-cc65/a2i_chess.h similarity index 100% rename from examples/template/a2i_chess.h rename to examples/30-dnd5eapi-cc65/a2i_chess.h diff --git a/examples/30-dnd5eapi-cc65/a2i_dnd5eapi.cpp b/examples/30-dnd5eapi-cc65/a2i_dnd5eapi.cpp new file mode 100644 index 0000000..8c1d6cc --- /dev/null +++ b/examples/30-dnd5eapi-cc65/a2i_dnd5eapi.cpp @@ -0,0 +1,88 @@ +#include +#include +#include +#include +#include +#include +#include "dnd5eapi_commands.h" +#include "a2i_dnd5eapi.h" + + +void Dnd5eapi::init(Apple2Idiot *a2ip, HTTPClient *httpp) { + a2i = a2ip; + http = httpp; + Serial.println("DND5EAPI()::init()"); +} + +byte Dnd5eapi::handleCommand(byte command) { + Serial.println("DND5EAPI() handleCommand"); + switch(command) { + case COMMAND_SEARCH_MONSTER: { + Serial.println("DND5EAPI() COMMAND_SEARCH_MONSTER"); + a2i->write_data(ESP_COMMAND_ADDRESS, ACK); // notify Apple IIe we are processing command byte + Serial.println("DND5EAPI() searchMonster()"); + return searchMonster(); + } + default: { + Serial.print("DND5EAPI() COMMAND_NOT_FOUND: "); + Serial.println(command); + return COMMAND_NOT_FOUND; + } + } +} + +byte Dnd5eapi::searchMonster() { + + byte result = 0; + Apple2Idiot a2i; + HTTPClient http; + + char api_request[MAX_STR_LEN]; + sprintf(api_request, "%s/?name=%s", api_entry_point, monster_search_string); + Serial.print(" "); Serial.println(api_request); + http.begin(api_request); + + int httpCode = http.GET(); //Make the request + Serial.print(" "); Serial.println("After GET()"); + delay(10); + Serial.print(" httpCode:"); Serial.println(httpCode); + + if (httpCode > 0) { //Check for the returning code + String payload = http.getString(); + //Serial.println(httpCode); + Serial.println("++++++++++++++++++++++++"); + Serial.println(payload); + Serial.println("++++++++++++++++++++++++"); + StaticJsonDocument<200> filter; + filter["count"] = true; + filter["results"] = true; + StaticJsonDocument<400> doc; + DeserializationError error = deserializeJson(doc, payload, DeserializationOption::Filter(filter)); + //DeserializationError error = deserializeJson(doc, payload); + if (error) { + Serial.print(F("deserializeJson() failed: ")); + Serial.println(error.f_str()); + } else { + Serial.println("----------------------"); + serializeJsonPretty(doc, Serial); + Serial.println("----------------------"); + serializeJsonPretty(doc["results"], Serial); + Serial.println("----------------------"); + String result_count = doc["count"]; + String results = doc["results"]; + //int address_counter = a2i.write_string_to_shared_ram(latitude, SHARED_RAM_START_ADDRESS); + //address_counter = a2i.write_string_to_shared_ram(longitude, address_counter + 1); + //address_counter = a2i.write_string_to_shared_ram(timestamp, address_counter + 1); + //address_counter = a2i.write_string_to_shared_ram(message, address_counter + 1); + } + result = ACK; + } + else { + Serial.println("Error on HTTP request"); + result = ERR; + } + + http.end(); //Free the resources + return result; +} + diff --git a/examples/30-dnd5eapi-cc65/a2i_dnd5eapi.h b/examples/30-dnd5eapi-cc65/a2i_dnd5eapi.h new file mode 100644 index 0000000..82183cd --- /dev/null +++ b/examples/30-dnd5eapi-cc65/a2i_dnd5eapi.h @@ -0,0 +1,42 @@ + +#ifndef A2I_DND5EAPI_H +#define A2I_DND5EAPI_H + +#include +#include +#include +#include +#include "dnd5eapi_commands.h" + +class Dnd5eapi { + + public: + // This is "registered" with A2I_commands.h which is part of Apple2Idiot.h This + // id is sent from the Apple to the ESP to tell the esp what app is currently + // active. The main loop of the ESP sketch then knows to use this class to + // respond to incoming commands from the Apple. + byte appId = APP_DND5EAPI; + + String monster_search_string = "crab"; + + void init(Apple2Idiot *a2ip, HTTPClient *httpp); + byte handleCommand(byte command); + byte searchMonster(byte command); + byte fetch_dnd5eapi(); + + private: + + Apple2Idiot *a2i; + HTTPClient *http; + + void removeSubstr (char *string, char *sub); + const char api_entry_point[49] = "https://www.dnd5eapi.co/api/"; + /* Remember, flexible array won't work + * in a class, so don't try to do this... + * const char foo[] = "hello world"; + */ + +}; + +#endif + diff --git a/examples/30-dnd5eapi-cc65/a2i_iss.cpp b/examples/30-dnd5eapi-cc65/a2i_iss.cpp new file mode 100644 index 0000000..c2fb02a --- /dev/null +++ b/examples/30-dnd5eapi-cc65/a2i_iss.cpp @@ -0,0 +1,92 @@ +#include +#include +#include +#include +#include +#include +#include "iss_commands.h" +#include "a2i_iss.h" + + +void Iss::init(Apple2Idiot *a2ip, HTTPClient *httpp) { + a2i = a2ip; + http = httpp; + Serial.println("ISS()::init()"); +} + +byte Iss::handleCommand(byte command) { + Serial.println("ISS() handleCommand"); + switch(command) { + case COMMAND_GET_ISS: { + Serial.println("ISS() COMMAND_GET_ISS"); + a2i->write_data(ESP_COMMAND_ADDRESS, ACK); // notify Apple IIe we are processing command byte + Serial.println("ISS() before fetch_iss()"); + return fetch_iss(); + } + default: { + Serial.print("ISS() COMMAND_NOT_FOUND: "); + Serial.println(command); + return COMMAND_NOT_FOUND; + } + } +} + +byte Iss::fetch_iss() { + + byte result = 0; + Apple2Idiot a2i; + HTTPClient http; + + char api_request[MAX_STR_LEN]; + sprintf(api_request, "%s", api_entry_point); + Serial.print(" "); Serial.println(api_request); + http.begin(api_request); + + int httpCode = http.GET(); //Make the request + Serial.print(" "); Serial.println("After GET()"); + delay(10); + Serial.print(" httpCode:"); Serial.println(httpCode); + + if (httpCode > 0) { //Check for the returning code + String payload = http.getString(); + //Serial.println(httpCode); + Serial.println("++++++++++++++++++++++++"); + Serial.println(payload); + Serial.println("++++++++++++++++++++++++"); + StaticJsonDocument<200> filter; + filter["iss_position"]["latitude"] = true; + filter["iss_position"]["longitude"] = true; + filter["timestamp"] = true; + filter["message"]["humidity"] = true; + StaticJsonDocument<400> doc; + DeserializationError error = deserializeJson(doc, payload, DeserializationOption::Filter(filter)); + //DeserializationError error = deserializeJson(doc, payload); + if (error) { + Serial.print(F("deserializeJson() failed: ")); + Serial.println(error.f_str()); + } else { + Serial.println("----------------------"); + serializeJsonPretty(doc, Serial); + Serial.println("----------------------"); + serializeJsonPretty(doc["iss_position"], Serial); + Serial.println("----------------------"); + String latitude = doc["iss_position"]["latitude"]; + String longitude = doc["iss_position"]["longitude"]; + String timestamp = doc["timestamp"]; + String message = doc["messasge"]; + int address_counter = a2i.write_string_to_shared_ram(latitude, SHARED_RAM_START_ADDRESS); + address_counter = a2i.write_string_to_shared_ram(longitude, address_counter + 1); + address_counter = a2i.write_string_to_shared_ram(timestamp, address_counter + 1); + address_counter = a2i.write_string_to_shared_ram(message, address_counter + 1); + } + result = ACK; + } + else { + Serial.println("Error on HTTP request"); + result = ERR; + } + + http.end(); //Free the resources + return result; +} + diff --git a/examples/30-dnd5eapi-cc65/a2i_iss.h b/examples/30-dnd5eapi-cc65/a2i_iss.h new file mode 100644 index 0000000..596bddc --- /dev/null +++ b/examples/30-dnd5eapi-cc65/a2i_iss.h @@ -0,0 +1,43 @@ +#ifndef A2I_ISS_H +#define A2I_ISS_H + +#include +#include +#include +#include +#include "iss_commands.h" + +class Iss { + + public: + // This is "registered" with A2I_commands.h which is part of Apple2Idiot.h This + // id is sent from the Apple to the ESP to tell the esp what app is currently + // active. The main loop of the ESP sketch then knows to use this class to + // respond to incoming commands from the Apple. + byte appId = APP_ISS; + + void init(Apple2Idiot *a2ip, HTTPClient *httpp); + //char* getGameStatus(char* game_status); + //byte makeMove(String move_string); + byte handleCommand(byte command); + byte fetch_iss(); + //byte validateMove(String move_string); + //char* getAIMove(); + //void getBoard(); + + private: + + Apple2Idiot *a2i; + HTTPClient *http; + + void removeSubstr (char *string, char *sub); + const char api_entry_point[49] = "http://api.open-notify.org/iss-now.json"; + /* Remember, flexible array won't work + * in a class, so don't try to do this... + * const char foo[] = "hello world"; + */ + +}; + +#endif + diff --git a/examples/30-dnd5eapi-cc65/a2i_weather.cpp b/examples/30-dnd5eapi-cc65/a2i_weather.cpp new file mode 100644 index 0000000..bcd7410 --- /dev/null +++ b/examples/30-dnd5eapi-cc65/a2i_weather.cpp @@ -0,0 +1,119 @@ +#include +#include +#include +#include +#include +#include +#include "weather_commands.h" +#include "a2i_weather.h" +#include "credentials.h" + + +void Weather::init(Apple2Idiot *a2ip, HTTPClient *httpp) { + a2i = a2ip; + http = httpp; + Serial.println("WEATHER()::init()"); +} + +byte Weather::handleCommand(byte command) { + Serial.println("WEATHER() handleCommand"); + switch(command) { + case COMMAND_FETCH_WEATHER: { + Serial.println("WEATHER() COMMAND_FETCH_WEATHER"); + a2i->write_data(ESP_COMMAND_ADDRESS, ACK); // notify Apple IIe we are processing command byte + Serial.println("WEATHER() before fetch_weather()"); + return fetch_weather(); + } + case COMMAND_SET_COUNTRY: { + Serial.println("WEATHER() COMMAND_SET_COUNTRY"); + a2i->write_data(ESP_COMMAND_ADDRESS, ACK); // notify Apple IIe we are processing command byte + country_code = a2i->read_string_from_ram(SHARED_RAM_START_ADDRESS); + Serial.println("Received CountryCode: ["+country_code+"]"); + a2i->write_data(APPLE_COMMAND_ADDRESS, ACK); + a2i->write_data(ESP_COMMAND_ADDRESS, EOT); // notify Apple IIe we are done processing command byte + return ACK; + //break; + } + case COMMAND_SET_CITY: { + //a2i->write_data(ESP_COMMAND_ADDRESS, ACK); // notify Apple IIe we are processing command byte + Serial.print("WEATHER() COMMAND_SET_CITY"); + a2i->write_data(ESP_COMMAND_ADDRESS, ACK); // notify Apple IIe we are processing command byte + city_name = a2i->read_string_from_ram(SHARED_RAM_START_ADDRESS); + Serial.println("Received CityName: ["+city_name+"]"); + a2i->write_data(APPLE_COMMAND_ADDRESS, ACK); + a2i->write_data(ESP_COMMAND_ADDRESS, EOT); // notify Apple IIe we are done processing command byte + return ACK; + } + default: { + Serial.print("WEATHER() COMMAND_NOT_FOUND: "); + Serial.println(command); + return COMMAND_NOT_FOUND; + } + } +} + +byte Weather::fetch_weather() { + + byte result = 0; + Apple2Idiot a2i; + HTTPClient http; + + char api_request[MAX_STR_LEN]; + sprintf(api_request, "%sq=%s,%s&APPID=%s", api_entry_point, city_name, country_code, WEATHER_ACCESS_TOKEN); + //sprintf(api_request, "http://api.openweathermap.org/data/2.5/weather?q=TUCSON,US&APPID=0ab97bbbea58592d7c9d64067a34d2d0"); + Serial.print(" "); Serial.println(api_request); + http.begin(api_request); + + int httpCode = http.GET(); //Make the request + Serial.print(" "); Serial.println("After GET()"); + delay(10); + + if (httpCode > 0) { //Check for the returning code + String payload = http.getString(); + //Serial.println(httpCode); + Serial.println("++++++++++++++++++++++++"); + Serial.println(payload); + Serial.println("++++++++++++++++++++++++"); + StaticJsonDocument<200> filter; + filter["weather"][0]["main"] = true; + filter["weather"][0]["description"] = true; + filter["main"]["humidity"] = true; + filter["main"]["temp"] = true; + filter["wind"]["speed"] = true; + filter["wind"]["deg"] = true; + StaticJsonDocument<400> doc; + DeserializationError error = deserializeJson(doc, payload, DeserializationOption::Filter(filter)); + //DeserializationError error = deserializeJson(doc, payload); + if (error) { + Serial.print(F("deserializeJson() failed: ")); + Serial.println(error.f_str()); + } else { + Serial.println("----------------------"); + serializeJsonPretty(doc, Serial); + Serial.println("----------------------"); + serializeJsonPretty(doc["wind"], Serial); + Serial.println("----------------------"); + String temp = doc["main"]["temp"]; + String humidity = doc["main"]["humidity"]; + String wind_speed = doc["wind"]["speed"]; + String wind_deg = doc["wind"]["deg"]; + String weather_description1 = doc["weather"][0]["main"]; + String weather_description2 = doc["weather"][0]["description"]; + int address_counter = a2i.write_string_to_shared_ram(temp, SHARED_RAM_START_ADDRESS); + address_counter = a2i.write_string_to_shared_ram(humidity, address_counter + 1); + address_counter = a2i.write_string_to_shared_ram(wind_speed, address_counter + 1); + address_counter = a2i.write_string_to_shared_ram(wind_deg, address_counter + 1); + address_counter = a2i.write_string_to_shared_ram(weather_description1, address_counter + 1); + address_counter = a2i.write_string_to_shared_ram(weather_description2, address_counter + 1); + } + result = ACK; + } + else { + Serial.println("Error on HTTP request"); + result = ERR; + } + + http.end(); //Free the resources + return result; +} + diff --git a/examples/30-dnd5eapi-cc65/a2i_weather.h b/examples/30-dnd5eapi-cc65/a2i_weather.h new file mode 100644 index 0000000..79eb8fe --- /dev/null +++ b/examples/30-dnd5eapi-cc65/a2i_weather.h @@ -0,0 +1,47 @@ +#ifndef A2I_WEATHER_H +#define A2I_WEATHER_H + +#include +#include +#include +#include +#include "weather_commands.h" +#include "credentials.h" + +class Weather { + + public: + // This is "registered" with A2I_commands.h which is part of Apple2Idiot.h This + // id is sent from the Apple to the ESP to tell the esp what app is currently + // active. The main loop of the ESP sketch then knows to use this class to + // respond to incoming commands from the Apple. + byte appId = APP_WEATHER; + + String country_code = "US"; + String city_name = "Tucson"; + + void init(Apple2Idiot *a2ip, HTTPClient *httpp); + //char* getGameStatus(char* game_status); + //byte makeMove(String move_string); + byte handleCommand(byte command); + byte fetch_weather(); + //byte validateMove(String move_string); + //char* getAIMove(); + //void getBoard(); + + private: + + Apple2Idiot *a2i; + HTTPClient *http; + + void removeSubstr (char *string, char *sub); + const char api_entry_point[49] = "http://api.openweathermap.org/data/2.5/weather?"; + /* Remember, flexible array won't work + * in a class, so don't try to do this... + * const char foo[] = "hello world"; + */ + +}; + +#endif + diff --git a/examples/template/chess_commands.h b/examples/30-dnd5eapi-cc65/chess_commands.h similarity index 100% rename from examples/template/chess_commands.h rename to examples/30-dnd5eapi-cc65/chess_commands.h diff --git a/examples/30-dnd5eapi-cc65/credentials.h.sample b/examples/30-dnd5eapi-cc65/credentials.h.sample new file mode 100644 index 0000000..cf506a6 --- /dev/null +++ b/examples/30-dnd5eapi-cc65/credentials.h.sample @@ -0,0 +1,12 @@ +#ifndef CREDENTIALS_H +#define CREDENTIALS_H + +#include "credentials.h" +// copy credentials.h.sample to credentials.h and edit +// so it contains your passwords and tokens. + +#define WIFI_SSID "XXXXXXXXXXXXXXX"; +#define WIFI_PASSWORD "XXXXXXXXXXXXXXX"; +#define WEATHER_ACCESS_TOKEN "XXXXXXXXXXXXXXX" + +#endif diff --git a/examples/30-dnd5eapi-cc65/dnd5eapi_commands.h b/examples/30-dnd5eapi-cc65/dnd5eapi_commands.h new file mode 100644 index 0000000..106603d --- /dev/null +++ b/examples/30-dnd5eapi-cc65/dnd5eapi_commands.h @@ -0,0 +1,9 @@ +#ifndef A2I_DND_COMMANDS_H +#define A2I_DND_COMMANDS_H + +/* Apple II <-> ESP Commands */ +#define COMMAND_SEARCH_MONSTER 20 + +/* Responses */ + +#endif diff --git a/examples/30-dnd5eapi-cc65/iss-tracker/.gitignore b/examples/30-dnd5eapi-cc65/iss-tracker/.gitignore new file mode 100644 index 0000000..1958a24 --- /dev/null +++ b/examples/30-dnd5eapi-cc65/iss-tracker/.gitignore @@ -0,0 +1,12 @@ +iss.xex +json_test +iss.s +iss.o +nsio.s +nsio.o +faux_json.s +faux_json.o +app_key.s +app_key.o +colors.s +colors.o diff --git a/examples/30-dnd5eapi-cc65/iss-tracker/apple2/Makefile b/examples/30-dnd5eapi-cc65/iss-tracker/apple2/Makefile new file mode 100644 index 0000000..521901c --- /dev/null +++ b/examples/30-dnd5eapi-cc65/iss-tracker/apple2/Makefile @@ -0,0 +1,353 @@ +############################################################################### +### Generic Makefile for cc65 projects - full version with abstract options ### +### V1.3.0(w) 2010 - 2013 Oliver Schmidt & Patryk "Silver Dream !" Łogiewa ### +############################################################################### + +############################################################################### +### In order to override defaults - values can be assigned to the variables ### +############################################################################### + +# Space or comma separated list of cc65 supported target platforms to build for. +# Default: c64 (lowercase!) +TARGETS := apple2 + +# Name of the final, single-file executable. +# Default: name of the current dir with target name appended +PROGRAM := iss + +# Path(s) to additional libraries required for linking the program +# Use only if you don't want to place copies of the libraries in SRCDIR +# Default: none +LIBS := + +# Custom linker issuration file +# Use only if you don't want to place it in SRCDIR +# Default: none +CONFIG := + +# Additional C compiler flags and options. +# Default: none +CFLAGS = -Os --static-locals -DBUILD_APPLE2 + +# Additional assembler flags and options. +# Default: none +ASFLAGS = + +# Additional linker flags and options. +# Default: none +LDFLAGS = --start-addr 0x4000 --ld-args -D,__HIMEM__=0xBF00 apple2-iobuf-0800.o + +# Path to the directory containing C and ASM sources. +# Default: src +SRCDIR := + +# Path to the directory where object files are to be stored (inside respective target subdirectories). +# Default: obj +OBJDIR := + +# Command used to run the emulator. +# Default: depending on target platform. For default (c64) target: x64 -kernal kernal -VICIIdsize -autoload +EMUCMD := + +# Optional commands used before starting the emulation process, and after finishing it. +# Default: none +#PREEMUCMD := osascript -e "tell application \"System Events\" to set isRunning to (name of processes) contains \"X11.bin\"" -e "if isRunning is true then tell application \"X11\" to activate" +#PREEMUCMD := osascript -e "tell application \"X11\" to activate" +#POSTEMUCMD := osascript -e "tell application \"System Events\" to tell process \"X11\" to set visible to false" +#POSTEMUCMD := osascript -e "tell application \"Terminal\" to activate" +PREEMUCMD := +POSTEMUCMD := + +# On Windows machines VICE emulators may not be available in the PATH by default. +# In such case, please set the variable below to point to directory containing +# VICE emulators. +#VICE_HOME := "C:\Program Files\WinVICE-2.2-x86\" +VICE_HOME := + +# Options state file name. You should not need to change this, but for those +# rare cases when you feel you really need to name it differently - here you are +STATEFILE := Makefile.options + +################################################################################### +#### DO NOT EDIT BELOW THIS LINE, UNLESS YOU REALLY KNOW WHAT YOU ARE DOING! #### +################################################################################### + +################################################################################### +### Mapping abstract options to the actual compiler, assembler and linker flags ### +### Predefined compiler, assembler and linker flags, used with abstract options ### +### valid for 2.14.x. Consult the documentation of your cc65 version before use ### +################################################################################### + +# Compiler flags used to tell the compiler to optimise for SPEED +define _optspeed_ + CFLAGS += -Oris +endef + +# Compiler flags used to tell the compiler to optimise for SIZE +define _optsize_ + CFLAGS += -Or +endef + +# Compiler and assembler flags for generating listings +define _listing_ + CFLAGS += --listing $$(@:.o=.lst) + ASFLAGS += --listing $$(@:.o=.lst) + REMOVES += $(addsuffix .lst,$(basename $(OBJECTS))) +endef + +# Linker flags for generating map file +define _mapfile_ + LDFLAGS += --mapfile $$@.map + REMOVES += $(PROGRAM).map +endef + +# Linker flags for generating VICE label file +define _labelfile_ + LDFLAGS += -Ln $$@.lbl + REMOVES += $(PROGRAM).lbl +endef + +# Linker flags for generating a debug file +define _debugfile_ + LDFLAGS += -Wl --dbgfile,$$@.dbg + REMOVES += $(PROGRAM).dbg +endef + +############################################################################### +### Defaults to be used if nothing defined in the editable sections above ### +############################################################################### + +# Presume the C64 target like the cl65 compile & link utility does. +# Set TARGETS to override. +ifeq ($(TARGETS),) + TARGETS := c64 +endif + +# Presume we're in a project directory so name the program like the current +# directory. Set PROGRAM to override. +ifeq ($(PROGRAM),) + PROGRAM := $(notdir $(CURDIR)) +endif + +# Presume the C and asm source files to be located in the subdirectory 'src'. +# Set SRCDIR to override. +ifeq ($(SRCDIR),) + SRCDIR := src +endif + +# Presume the object and dependency files to be located in the subdirectory +# 'obj' (which will be created). Set OBJDIR to override. +ifeq ($(OBJDIR),) + OBJDIR := obj +endif +TARGETOBJDIR := $(OBJDIR)/$(TARGETS) + +# On Windows it is mandatory to have CC65_HOME set. So do not unnecessarily +# rely on cl65 being added to the PATH in this scenario. +ifdef CC65_HOME + CC := $(CC65_HOME)/bin/cl65 +else + CC := cl65 +endif + +# Default emulator commands and options for particular targets. +# Set EMUCMD to override. +c64_EMUCMD := $(VICE_HOME)xscpu64 -VICIIdsize -autostart +c128_EMUCMD := $(VICE_HOME)x128 -kernal kernal -VICIIdsize -autoload +vic20_EMUCMD := $(VICE_HOME)xvic -kernal kernal -VICdsize -autoload +pet_EMUCMD := $(VICE_HOME)xpet -Crtcdsize -autoload +plus4_EMUCMD := $(VICE_HOME)xplus4 -TEDdsize -autoload +# So far there is no x16 emulator in VICE (why??) so we have to use xplus4 with -memsize option +c16_EMUCMD := $(VICE_HOME)xplus4 -ramsize 16 -TEDdsize -autoload +cbm510_EMUCMD := $(VICE_HOME)xcbm2 -model 510 -VICIIdsize -autoload +cbm610_EMUCMD := $(VICE_HOME)xcbm2 -model 610 -Crtcdsize -autoload +atari_EMUCMD := atari800 -windowed -xl -pal -nopatchall -run + +ifeq ($(EMUCMD),) + EMUCMD = $($(CC65TARGET)_EMUCMD) +endif + +############################################################################### +### The magic begins ### +############################################################################### + +# The "Native Win32" GNU Make contains quite some workarounds to get along with +# cmd.exe as shell. However it does not provide means to determine that it does +# actually activate those workarounds. Especially does $(SHELL) NOT contain the +# value 'cmd.exe'. So the usual way to determine if cmd.exe is being used is to +# execute the command 'echo' without any parameters. Only cmd.exe will return a +# non-empy string - saying 'ECHO is on/off'. +# +# Many "Native Win32" prorams accept '/' as directory delimiter just fine. How- +# ever the internal commands of cmd.exe generally require '\' to be used. +# +# cmd.exe has an internal command 'mkdir' that doesn't understand nor require a +# '-p' to create parent directories as needed. +# +# cmd.exe has an internal command 'del' that reports a syntax error if executed +# without any file so make sure to call it only if there's an actual argument. +ifeq ($(shell echo),) + MKDIR = mkdir -p $1 + RMDIR = rmdir $1 + RMFILES = $(RM) $1 +else + MKDIR = mkdir $(subst /,\,$1) + RMDIR = rmdir $(subst /,\,$1) + RMFILES = $(if $1,del /f $(subst /,\,$1)) +endif +COMMA := , +SPACE := $(N/A) $(N/A) +define NEWLINE + + +endef +# Note: Do not remove any of the two empty lines above ! + +TARGETLIST := $(subst $(COMMA),$(SPACE),$(TARGETS)) + +ifeq ($(words $(TARGETLIST)),1) + +# Set PROGRAM to something like 'myprog.c64'. +override PROGRAM := $(PROGRAM) + +# Set SOURCES to something like 'src/foo.c src/bar.s'. +# Use of assembler files with names ending differently than .s is deprecated! +SOURCES := $(wildcard $(SRCDIR)/*.c) +SOURCES += $(wildcard $(SRCDIR)/*.s) +SOURCES += $(wildcard $(SRCDIR)/*.asm) +SOURCES += $(wildcard $(SRCDIR)/*.a65) + +# Add to SOURCES something like 'src/c64/me.c src/c64/too.s'. +# Use of assembler files with names ending differently than .s is deprecated! +SOURCES += $(wildcard $(SRCDIR)/$(TARGETLIST)/*.c) +SOURCES += $(wildcard $(SRCDIR)/$(TARGETLIST)/*.s) +SOURCES += $(wildcard $(SRCDIR)/$(TARGETLIST)/*.asm) +SOURCES += $(wildcard $(SRCDIR)/$(TARGETLIST)/*.a65) + +# Set OBJECTS to something like 'obj/c64/foo.o obj/c64/bar.o'. +OBJECTS := $(addsuffix .o,$(basename $(addprefix $(TARGETOBJDIR)/,$(notdir $(SOURCES))))) + +# Set DEPENDS to something like 'obj/c64/foo.d obj/c64/bar.d'. +DEPENDS := $(OBJECTS:.o=.d) + +# Add to LIBS something like 'src/foo.lib src/c64/bar.lib'. +LIBS += $(wildcard $(SRCDIR)/*.lib) +LIBS += $(wildcard $(SRCDIR)/$(TARGETLIST)/*.lib) + +# Add to CONFIG something like 'src/c64/bar.cfg src/foo.cfg'. +CONFIG += $(wildcard $(SRCDIR)/$(TARGETLIST)/*.cfg) +CONFIG += $(wildcard $(SRCDIR)/*.cfg) + +# Select CONFIG file to use. Target specific isss have higher priority. +ifneq ($(word 2,$(CONFIG)),) + CONFIG := $(firstword $(CONFIG)) + $(info Using iss file $(CONFIG) for linking) +endif + +.SUFFIXES: +.PHONY: all test clean zap love + +all: $(PROGRAM) + +-include $(DEPENDS) +-include $(STATEFILE) + +# If OPTIONS are given on the command line then save them to STATEFILE +# if (and only if) they have actually changed. But if OPTIONS are not +# given on the command line then load them from STATEFILE. Have object +# files depend on STATEFILE only if it actually exists. +ifeq ($(origin OPTIONS),command line) + ifneq ($(OPTIONS),$(_OPTIONS_)) + ifeq ($(OPTIONS),) + $(info Removing OPTIONS) + $(shell $(RM) $(STATEFILE)) + $(eval $(STATEFILE):) + else + $(info Saving OPTIONS=$(OPTIONS)) + $(shell echo _OPTIONS_=$(OPTIONS) > $(STATEFILE)) + endif + $(eval $(OBJECTS): $(STATEFILE)) + endif +else + ifeq ($(origin _OPTIONS_),file) + $(info Using saved OPTIONS=$(_OPTIONS_)) + OPTIONS = $(_OPTIONS_) + $(eval $(OBJECTS): $(STATEFILE)) + endif +endif + +# Transform the abstract OPTIONS to the actual cc65 options. +$(foreach o,$(subst $(COMMA),$(SPACE),$(OPTIONS)),$(eval $(_$o_))) + +# Strip potential variant suffix from the actual cc65 target. +CC65TARGET := $(firstword $(subst .,$(SPACE),$(TARGETLIST))) + +# The remaining targets. +$(TARGETOBJDIR): + $(call MKDIR,$@) + +vpath %.c $(SRCDIR)/$(TARGETLIST) $(SRCDIR) + +$(TARGETOBJDIR)/%.o: %.c | $(TARGETOBJDIR) + $(CC) -t $(CC65TARGET) -c --create-dep $(@:.o=.d) $(CFLAGS) -o $@ $< + +vpath %.s $(SRCDIR)/$(TARGETLIST) $(SRCDIR) + +$(TARGETOBJDIR)/%.o: %.s | $(TARGETOBJDIR) + $(CC) -t $(CC65TARGET) -c --create-dep $(@:.o=.d) $(ASFLAGS) -o $@ $< + +vpath %.asm $(SRCDIR)/$(TARGETLIST) $(SRCDIR) + +$(TARGETOBJDIR)/%.o: %.asm | $(TARGETOBJDIR) + $(CC) -t $(CC65TARGET) -c --create-dep $(@:.o=.d) $(ASFLAGS) -o $@ $< + +vpath %.a65 $(SRCDIR)/$(TARGETLIST) $(SRCDIR) + +$(TARGETOBJDIR)/%.o: %.a65 | $(TARGETOBJDIR) + $(CC) -t $(CC65TARGET) -c --create-dep $(@:.o=.d) $(ASFLAGS) -o $@ $< + +$(PROGRAM): $(CONFIG) $(OBJECTS) $(LIBS) + $(CC) -t $(CC65TARGET) $(LDFLAGS) -o $@ $(patsubst %.cfg,-C %.cfg,$^) + +test: $(PROGRAM) + $(PREEMUCMD) + $(EMUCMD) $< + $(POSTEMUCMD) + +dist: $(PROGRAM) + cp dist.apple2/bootable.po dist.apple2/dist.po + java -jar dist.apple2/ac.jar -p dist.apple2/dist.po iss.system sys $(STATEFILE)) + endif + $(eval $(OBJECTS): $(STATEFILE)) + endif +else + ifeq ($(origin _OPTIONS_),file) + $(info Using saved OPTIONS=$(_OPTIONS_)) + OPTIONS = $(_OPTIONS_) + $(eval $(OBJECTS): $(STATEFILE)) + endif +endif + +# Transform the abstract OPTIONS to the actual cc65 options. +$(foreach o,$(subst $(COMMA),$(SPACE),$(OPTIONS)),$(eval $(_$o_))) + +# Strip potential variant suffix from the actual cc65 target. +CC65TARGET := $(firstword $(subst .,$(SPACE),$(TARGETLIST))) + +# The remaining targets. +$(TARGETOBJDIR): + $(call MKDIR,$@) + +vpath %.c $(SRCDIR)/$(TARGETLIST) $(SRCDIR) + +$(TARGETOBJDIR)/%.o: %.c | $(TARGETOBJDIR) + $(CC) -t $(CC65TARGET) -c --create-dep $(@:.o=.d) $(CFLAGS) -o $@ $< + +vpath %.s $(SRCDIR)/$(TARGETLIST) $(SRCDIR) + +$(TARGETOBJDIR)/%.o: %.s | $(TARGETOBJDIR) + $(CC) -t $(CC65TARGET) -c --create-dep $(@:.o=.d) $(ASFLAGS) -o $@ $< + +vpath %.asm $(SRCDIR)/$(TARGETLIST) $(SRCDIR) + +$(TARGETOBJDIR)/%.o: %.asm | $(TARGETOBJDIR) + $(CC) -t $(CC65TARGET) -c --create-dep $(@:.o=.d) $(ASFLAGS) -o $@ $< + +vpath %.a65 $(SRCDIR)/$(TARGETLIST) $(SRCDIR) + +$(TARGETOBJDIR)/%.o: %.a65 | $(TARGETOBJDIR) + $(CC) -t $(CC65TARGET) -c --create-dep $(@:.o=.d) $(ASFLAGS) -o $@ $< + +$(PROGRAM): $(CONFIG) $(OBJECTS) $(LIBS) + $(CC) -t $(CC65TARGET) $(LDFLAGS) -o $@ $(patsubst %.cfg,-C %.cfg,$^) + +test: $(PROGRAM) + $(PREEMUCMD) + $(EMUCMD) $< + $(POSTEMUCMD) + +dist: $(PROGRAM) + cp dist.apple2/bootable.po dist.apple2/dist.po + java -jar dist.apple2/ac.jar -p dist.apple2/dist.po iss.system sys +#include + +// Ethernet driver initialization parameter values +// +#if defined(__APPLE2__) +#define ETH_INIT_DEFAULT 3 // Apple II slot number +#elif defined(__ATARI__) +#define ETH_INIT_DEFAULT 8 // ATARI PBI device ID +#else +#define ETH_INIT_DEFAULT 0 // Unused +#endif + +// Initialize the IP stack +// +// This calls the individual protocol & driver initializations, so this is +// the only *_init routine that must be called by a user application, +// except for dhcp_init which must also be called if the application +// is using DHCP rather than hardcoded IP configuration. +// +// Inputs: eth_init: Ethernet driver initialization parameter +// Output: true if there was an error, false otherwise +// +bool __fastcall__ ip65_init(uint8_t eth_init); + +// Access to Ethernet configuration +// +// Access to the two items below is only valid after ip65_init returned false. +// +extern uint8_t cfg_mac[6]; // MAC address of local machine +extern char eth_name[]; // Zero terminated string containing Ethernet driver name + +// Error codes +// +#define IP65_ERROR_PORT_IN_USE 0x80 +#define IP65_ERROR_TIMEOUT_ON_RECEIVE 0x81 +#define IP65_ERROR_TRANSMIT_FAILED 0x82 +#define IP65_ERROR_TRANSMISSION_REJECTED_BY_PEER 0x83 +#define IP65_ERROR_NAME_TOO_LONG 0x84 +#define IP65_ERROR_DEVICE_FAILURE 0x85 +#define IP65_ERROR_ABORTED_BY_USER 0x86 +#define IP65_ERROR_LISTENER_NOT_AVAILABLE 0x87 +#define IP65_ERROR_CONNECTION_RESET_BY_PEER 0x89 +#define IP65_ERROR_CONNECTION_CLOSED 0x8A +#define IP65_ERROR_MALFORMED_URL 0xA0 +#define IP65_ERROR_DNS_LOOKUP_FAILED 0xA1 + +// Last error code +// +extern uint8_t ip65_error; + +// Convert error code into a string describing the error +// +// The pointer returned is a static string, which mustn't be modified. +// +// Inputs: err_code: Error code +// Output: Zero terminated string describing the error +// +char* __fastcall__ ip65_strerror(uint8_t err_code); + +// Main IP polling loop +// +// This routine should be periodically called by an application at any time +// that an inbound packet needs to be handled. +// It is 'non-blocking', i.e. it will return if there is no packet waiting to be +// handled. Any inbound packet will be handed off to the appropriate handler. +// +// Inputs: None +// Output: true if no packet was waiting or packet handling caused error, false otherwise +// +bool ip65_process(void); + +// Generate a 'random' 16 bit word +// +// Entropy comes from the last ethernet frame, counters, and timer. +// +// Inputs: None +// Output: Pseudo-random 16 bit number +// +uint16_t ip65_random_word(void); + +// Convert 4 octets (IP address, netmask) into a string representing a dotted quad +// +// The string is returned in a statically allocated buffer, which subsequent calls +// will overwrite. +// +// Inputs: quad: IP address +// Output: Zero terminated string containing dotted quad (e.g. "192.168.1.0") +// +char* __fastcall__ dotted_quad(uint32_t quad); + +// Convert a string representing a dotted quad (IP address, netmask) into 4 octets +// +// Inputs: quad: Zero terminated string containing dotted quad (e.g. "192.168.1.0"), +// to simplify URL parsing, a ':' or '/' can also terminate the string. +// Output: IP address, 0 on error +// +uint32_t __fastcall__ parse_dotted_quad(char* quad); + +// Minimal DHCP client implementation +// +// IP addresses are requested from a DHCP server (aka 'leased') but are not renewed +// or released. Although this is not correct behaviour according to the DHCP RFC, +// this works fine in practice in a typical home network environment. +// +// Inputs: None (although ip65_init should be called first) +// Output: false if IP config has been sucesfully obtained and cfg_ip, cfg_netmask, +// cfg_gateway and cfg_dns will be set per response from dhcp server. +// dhcp_server will be set to address of server that provided configuration. +// true if there was an error +// +bool dhcp_init(void); + +// Access to IP configuration +// +// The five items below will be overwritten if dhcp_init is called. +// +extern uint32_t cfg_ip; // IP address of local machine +extern uint32_t cfg_netmask; // Netmask of local network +extern uint32_t cfg_gateway; // IP address of router on local network +extern uint32_t cfg_dns; // IP address of DNS server to use +extern uint32_t dhcp_server; // Address of DHCP server that config was obtained from + +// Resolve a string containing a hostname (or a dotted quad) to an IP address +// +// Inputs: hostname: Zero terminated string containing either a DNS hostname +// (e.g. "host.example.com") or an address in "dotted quad" +// format (e.g. "192.168.1.0") +// Output: IP address of the hostname, 0 on error +// +uint32_t __fastcall__ dns_resolve(const char* hostname); + +// Send a ping (ICMP echo request) to a remote host, and wait for a response +// +// Inputs: dest: Destination IP address +// Output: 0 if no response, otherwise time (in miliseconds) for host to respond +// +uint16_t __fastcall__ icmp_ping(uint32_t dest); + +// Add a UDP listener +// +// Inputs: port: UDP port to listen on +// callback: Vector to call when UDP packet arrives on specified port +// Output: true if too may listeners already installed, false otherwise +// +bool __fastcall__ udp_add_listener(uint16_t port, void (*callback)(void)); + +// Remove a UDP listener +// +// Inputs: port: UDP port to stop listening on +// Output: false if handler found and removed, +// true if handler for specified port not found +// +bool __fastcall__ udp_remove_listener(uint16_t port); + +// Access to received UDP packet +// +// Access to the four items below is only valid in the context of a callback +// added with udp_add_listener. +// +extern uint8_t udp_recv_buf[1476]; // Buffer with data received + uint16_t udp_recv_len(void); // Length of data received + uint32_t udp_recv_src(void); // Source IP address + uint16_t udp_recv_src_port(void); // Source port + +// Send a UDP packet +// +// If the correct MAC address can't be found in the ARP cache then +// an ARP request is sent - and the UDP packet is NOT sent. The caller +// should wait a while calling ip65_process (to allow time for an ARP +// response to arrive) and then call upd_send again. This behavior +// makes sense as a UDP packet may get lost in transit at any time +// so the caller should to be prepared to resend it after a while +// anyway. +// +// Inputs: buf: Pointer to buffer containing data to be sent +// len: Length of data to send (exclusive of any headers) +// dest: Destination IP address +// dest_port: Destination port +// src_port: Source port +// Output: true if an error occured, false otherwise +// +bool __fastcall__ udp_send(const uint8_t* buf, uint16_t len, uint32_t dest, + uint16_t dest_port, uint16_t src_port); + +// Listen for an inbound TCP connection +// +// This is a 'blocking' call, i.e. it will not return until a connection has been made. +// +// Inputs: port: TCP port to listen on +// callback: Vector to call when data arrives on this connection +// buf: Pointer to buffer with data received +// len: -1 on close, otherwise length of data received +// Output: IP address of the connected client, 0 on error +// +uint32_t __fastcall__ tcp_listen(uint16_t port, + void __fastcall__ (*callback)(const uint8_t* buf, + int16_t len)); + +// Make outbound TCP connection +// +// Inputs: dest: Destination IP address +// dest_port: Destination port +// callback: Vector to call when data arrives on this connection +// buf: Pointer to buffer with data received +// len: -1 on close, otherwise length of data received +// Output: true if an error occured, false otherwise +// +bool __fastcall__ tcp_connect(uint32_t dest, uint16_t dest_port, + void __fastcall__ (*callback)(const uint8_t* buf, + int16_t len)); + +// Close the current TCP connection +// +// Inputs: None +// Output: true if an error occured, false otherwise +// +bool tcp_close(void); + +// Send data on the current TCP connection +// +// Inputs: buf: Pointer to buffer containing data to be sent +// len: Length of data to send (up to 1460 bytes) +// Output: true if an error occured, false otherwise +// +bool __fastcall__ tcp_send(const uint8_t* buf, uint16_t len); + +// Send an empty ACK packet on the current TCP connection +// +// Inputs: None +// Output: true if an error occured, false otherwise +// +bool tcp_send_keep_alive(void); + +// Query an SNTP server for current UTC time +// +// Inputs: SNTP server IP address +// Output: The number of seconds since 00:00 on Jan 1 1900 (UTC), 0 on error +// +uint32_t __fastcall__ sntp_get_time(uint32_t server); + +// Download a file from a TFTP server and provide data to user supplied vector +// +// Inputs: server: IP address of server to receive file from +// name: Zero terminated string containing the name of file to download +// callback: Vector to call once for each 512 byte packet received +// buf: Pointer to buffer containing data received +// len: 512 if buffer is full, otherwise number of bytes +// in the buffer +// Output: true if an error occured, false otherwise +// +bool __fastcall__ tftp_download(uint32_t server, const char* name, + void __fastcall__ (*callback)(const uint8_t* buf, + uint16_t len)); + +// Download a file from a TFTP server and provide data to specified memory location +// +// Inputs: server: IP address of server to receive file from +// name: Zero terminated string containing the name of file to download +// buf: Pointer to buffer containing data received +// Output: Length of data received, 0 on error +// +uint16_t __fastcall__ tftp_download_to_memory(uint32_t server, const char* name, + const uint8_t* buf); + +// Upload a file to a TFTP server with data retrieved from user supplied vector +// +// Inputs: server: IP address of server to send file to +// name: Zero terminated string containing the name of file to upload +// callback: Vector to call once for each 512 byte packet to be sent +// buf: Pointer to buffer containing data to be sent +// Output: 512 if buffer is full, otherwise number of bytes +// in the buffer +// Output: true if an error occured, false otherwise +// +bool __fastcall__ tftp_upload(uint32_t server, const char* name, + uint16_t __fastcall__ (*callback)(const uint8_t* buf)); + +// Upload a file to a TFTP server with data retrieved from specified memory location +// +// Inputs: server: IP address of server to send file to +// name: Zero terminated string containing the name of file to upload +// buf: Pointer to buffer containing data to be sent +// len: Length of data to be sent +// Output: true if an error occured, false otherwise +// +bool __fastcall__ tftp_upload_from_memory(uint32_t server, const char* name, + const uint8_t* buf, uint16_t len); + +// Parse an HTTP URL into a form that makes it easy to retrieve the specified resource +// +// On success the variables url_ip, url_port and url_selector (see below) are valid. +// +// Inputs: url: Zero (or ctrl char) terminated string containing the URL +// resolve: Resolve host in URL +// Output: true if an error occured, false otherwise +// +bool __fastcall__ url_parse(const char* url, bool resolve); + +// Access to parsed HTTP URL +// +// Access to the four items below is only valid after url_parse returned false. +// +extern char* url_host; // Zero terminated string containing host in URL + "\r\n\r\n" +extern uint32_t url_ip; // IP address of host in URL (only if 'resolve' is true) +extern uint16_t url_port; // Port number of URL +extern char* url_selector; // Zero terminated string containing selector part of URL + +// Download a resource specified by an HTTP URL +// +// The URL mustn't be longer than 1400 chars. The buffer is temporarily used to hold the +// generated HTTP request so it should have a length of at least 1460 bytes. On success +// the resource is zero terminated. +// +// Inputs: url: Zero (or ctrl char) terminated string containing the URL +// buf: Pointer to a buffer that the resource will be downloaded into +// len: Length of buffer +// Output: Length of resource downloaded, 0 on error +// +uint16_t __fastcall__ url_download(const char* url, const uint8_t* buf, uint16_t len); + +// Start an HTTP server +// +// This routine will stay in an endless loop that is broken only if user press the abort key. +// +// Inputs: port: TCP port to listen on +// callback: Vector to call for each inbound HTTP request +// client: IP address of the client that sent the request +// method: Zero terminated string containing the HTTP method +// path: Zero terminated string containing the HTTP path +// Output: None +// +void __fastcall__ httpd_start(uint16_t port, + void __fastcall__ (*callback)(uint32_t client, + const char* method, + const char* path)); + +// HTTP response types +// +#define HTTPD_RESPONSE_NOHEADER 0 // No HTTP response header +#define HTTPD_RESPONSE_200_TEXT 1 // HTTP Code: 200 OK, Content Type: 'text/text' +#define HTTPD_RESPONSE_200_HTML 2 // HTTP Code: 200 OK, Content Type: 'text/html' +#define HTTPD_RESPONSE_200_DATA 3 // HTTP Code: 200 OK, Content Type: 'application/octet-stream' +#define HTTPD_RESPONSE_404 4 // HTTP Code: 404 Not Found +#define HTTPD_RESPONSE_500 5 // HTTP Code: 500 System Error + +// Send HTTP response +// +// Calling httpd_send_response is only valid in the context of a httpd_start callback. +// For the response types HTTPD_RESPONSE_404 and HTTPD_RESPONSE_500 'buf' is ignored. +// With the response type HTTPD_RESPONSE_NOHEADER it's possible to add more content to +// an already sent HTTP response. +// +// Inputs: response_type: Value describing HTTP code and content type in response header +// buf: Pointer to buffer with HTTP response content +// len: Length of buffer with HTTP response content +// Output: None +// +void __fastcall__ httpd_send_response(uint8_t response_type, + const uint8_t* buf, uint16_t len); + +// Retrieve the value of a variable defined in the previously received HTTP request +// +// Calling http_get_value is only valid in the context of a httpd_start callback. +// Only the first letter in a variable name is significant. E.g. if a querystring contains +// the variables 'a','alpha' and 'alabama', then only the first one will be retrievable. +// +// Inputs: name: Variable to retrieve +// Output: Variable value (zero terminated string) if variable exists, null otherwise. +// +char* __fastcall__ http_get_value(char name); + +// Get number of milliseconds since initialization +// +// Inputs: None +// Output: Current number of milliseconds +// +uint16_t timer_read(void); + +// Check if specified period of time has passed yet +// +// Inputs: time: Number of milliseconds we are willing to wait for +// Output: true if timeout occured, false otherwise +// +bool __fastcall__ timer_timeout(uint16_t time); + +// Check whether the abort key is being pressed +// +// Inputs: None +// Output: true if abort key pressed, false otherwise +// +bool input_check_for_abort_key(void); + +// Control abort key +// +// Control if the user can abort blocking functions with the abort key +// (making them return IP65_ERROR_ABORTED_BY_USER). Initially the abort +// key is enabled. +// +// Inputs: enable: false to disable the key, true to enable the key +// Output: None +// +void __fastcall__ input_set_abort_key(bool enable); + +// Access to actual abort key code +// +extern uint8_t abort_key; + +#endif diff --git a/examples/30-dnd5eapi-cc65/iss-tracker/apple2/src/apple2.ip65/main.c b/examples/30-dnd5eapi-cc65/iss-tracker/apple2/src/apple2.ip65/main.c new file mode 100644 index 0000000..d9e7533 --- /dev/null +++ b/examples/30-dnd5eapi-cc65/iss-tracker/apple2/src/apple2.ip65/main.c @@ -0,0 +1,84 @@ +/** + * ISS Tracker + * + * @author Thomas Cherryhomes + * @email thom dot cherryhomes at gmail dot com + * @license gpl v. 3, see LICENSE.md for details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "map.h" +#include "satellite.h" +#include "osd.h" +#include "ip65.h" + +unsigned char net = ETH_INIT_DEFAULT; +char lon_s[16], lat_s[16]; +int lat, lon; +long timer; +unsigned long ts; + +void error_exit(char *op) +{ + printf("%s:%s\n",op,ip65_strerror(ip65_error)); + cgetc(); + exit(1); +} + +void main(void) +{ + int f; + f = open("ethernet.slot",O_RDONLY); + if (f!=-1) + { + read(f,&net,sizeof(net)); + close(f); + net &= ~'0'; + } + + if (ip65_init(net)) + error_exit("ip65_init"); + + if (dhcp_init()) + error_exit("dhcp_init"); + + tgi_install(tgi_static_stddrv); + tgi_init(); + tgi_apple2_mix(true); + tgi_clear(); + + while (1) + { + timer=524088; + clrscr(); + satellite_fetch(&lon,&lat,lon_s,lat_s,&ts); + map(); + osd(lon_s,lat_s,ts); + satellite(lon,lat); + + while (timer>0) + { + if (kbhit()) + switch(cgetc()) + { + case 0x1b: + return; + case 0x0D: + case 0x0A: + timer=0; + break; + default: + break; + } + + timer--; + } + } +} diff --git a/examples/30-dnd5eapi-cc65/iss-tracker/apple2/src/apple2.ip65/map.h b/examples/30-dnd5eapi-cc65/iss-tracker/apple2/src/apple2.ip65/map.h new file mode 100644 index 0000000..d176ef9 --- /dev/null +++ b/examples/30-dnd5eapi-cc65/iss-tracker/apple2/src/apple2.ip65/map.h @@ -0,0 +1,14 @@ +/** + * ISS Tracker + * + * @author Thomas Cherryhomes + * @email thom dot cherryhomes at gmail dot com + * @license gpl v. 3, see LICENSE.md for details. + */ + +#ifndef MAP_H +#define MAP_H + +void map(void); + +#endif /* MAP_H */ diff --git a/examples/30-dnd5eapi-cc65/iss-tracker/apple2/src/apple2.ip65/osd.h b/examples/30-dnd5eapi-cc65/iss-tracker/apple2/src/apple2.ip65/osd.h new file mode 100644 index 0000000..183d57c --- /dev/null +++ b/examples/30-dnd5eapi-cc65/iss-tracker/apple2/src/apple2.ip65/osd.h @@ -0,0 +1,14 @@ +/** + * ISS Tracker + * + * @author Thomas Cherryhomes + * @email thom dot cherryhomes at gmail dot com + * @license gpl v. 3, see LICENSE.md for details. + */ + +#ifndef OSD_H +#define OSD_H + +void osd(char *lon, char *lat, unsigned long ts); + +#endif /* OSD_H */ diff --git a/examples/30-dnd5eapi-cc65/iss-tracker/apple2/src/apple2.ip65/satellite.c b/examples/30-dnd5eapi-cc65/iss-tracker/apple2/src/apple2.ip65/satellite.c new file mode 100644 index 0000000..50061fb --- /dev/null +++ b/examples/30-dnd5eapi-cc65/iss-tracker/apple2/src/apple2.ip65/satellite.c @@ -0,0 +1,100 @@ +/** + * ISS Tracker + * + * @author Thomas Cherryhomes + * @email thom dot cherryhomes at gmail dot com + * @license gpl v. 3, see LICENSE.md for details. + */ + +#define CENTER_X 4 +#define CENTER_Y 4 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "satellite.h" +#include "ip65.h" + +const unsigned char _satellite[8] = { + 0x20, /* ..X.|.... */ + 0x50, /* .X.X|.... */ + 0xA4, /* X.X.|.X.. */ + 0x58, /* .X.X|X... */ + 0x1A, /* ...X|X.X. */ + 0x05, /* ....|.X.X */ + 0x0A, /* ....|X.X. */ + 0x04, /* ....|.X.. */ +}; + +// TODO: These tables are broken, recalculate them! + +unsigned char xpos[360] = + { 0,0,1,2,3,3,4,5,6,7,7,8,9,10,10,11,12,13,14,14,15,16,17,17,18,19,20,21,21,22,23,24,24,25,26,27,28,28,29,30,31,31,32,33,34,35,35,36,37,38,38,39,40,41,42,42,43,44,45,45,46,47,48,49,49,50,51,52,52,53,54,55,56,56,57,58,59,59,60,61,62,63,63,64,65,66,66,67,68,69,70,70,71,72,73,73,74,75,76,77,77,78,79,80,80,81,82,83,84,84,85,86,87,87,88,89,90,91,91,92,93,94,94,95,96,97,98,98,99,100,101,101,102,103,104,105,105,106,107,108,108,109,110,111,112,112,113,114,115,115,116,117,118,119,119,120,121,122,122,123,124,125,126,126,127,128,129,129,130,131,132,133,133,134,135,136,136,137,138,139,140,140,141,142,143,143,144,145,146,147,147,148,149,150,150,151,152,153,154,154,155,156,157,157,158,159,160,161,161,162,163,164,164,165,166,167,168,168,169,170,171,171,172,173,174,175,175,176,177,178,178,179,180,181,182,182,183,184,185,185,186,187,188,189,189,190,191,192,192,193,194,195,196,196,197,198,199,199,200,201,202,203,203,204,205,206,206,207,208,209,210,210,211,212,213,213,214,215,216,217,217,218,219,220,220,221,222,223,224,224,225,226,227,227,228,229,230,231,231,232,233,234,234,235,236,237,238,238,239,240,241,241,242,243,244,245,245,246,247,248,248,249,250,251,252,252,253,254,255,255,0,1,2,3,3,4,5,6,6,7,8,9,10,10,11,12,13,13,14,15,16,17,17,18,19,20,20,21,22,23, }; + +unsigned char ypos[360] = + { 0,0,0,1,1,2,2,3,3,4,4,4,5,5,6,6,7,7,8,8,8,9,9,10,10,11,11,12,12,12,13,13,14,14,15,15,16,16,16,17,17,18,18,19,19,20,20,20,21,21,22,22,23,23,24,24,24,25,25,26,26,27,27,28,28,28,29,29,30,30,31,31,32,32,32,33,33,34,34,35,35,36,36,36,37,37,38,38,39,39,40,40,40,41,41,42,42,43,43,44,44,44,45,45,46,46,47,47,48,48,48,49,49,50,50,51,51,52,52,52,53,53,54,54,55,55,56,56,56,57,57,58,58,59,59,60,60,60,61,61,62,62,63,63,64,64,64,65,65,66,66,67,67,68,68,68,69,69,70,70,71,71,72,72,72,73,73,74,74,75,75,76,76,76,77,77,78,78,79,79,80,80,80,81,81,82,82,83,83,84,84,84,85,85,86,86,87,87,88,88,88,89,89,90,90,91,91,92,92,92,93,93,94,94,95,95,96,96,96,97,97,98,98,99,99,100,100,100,101,101,102,102,103,103,104,104,104,105,105,106,106,107,107,108,108,108,109,109,110,110,111,111,112,112,112,113,113,114,114,115,115,116,116,116,117,117,118,118,119,119,120,120,120,121,121,122,122,123,123,124,124,124,125,125,126,126,127,127,128,128,128,129,129,130,130,131,131,132,132,132,133,133,134,134,135,135,136,136,136,137,137,138,138,139,139,140,140,140,141,141,142,142,143,143,144,144,144,145,145,146,146,147,147,148,148,148,149,149,150,150,151,151,152,152,152,153,153,154,154,155,155,156,156,156,157,157,158,158,159,159, }; + +const char url[]="HTTP://api.open-notify.org/iss-now.json"; +const char fmt[]="{\"iss_position\": {\"latitude\": \"%s \"longitude\": \"%s \"timestamp\": %ld, \"message\": \"success\"}"; + +char download[1024]; + +int post_proc(char *s) +{ + char *c = strchr(s,'"'); + *c = 0; + return atoi(s); +} + +bool satellite_fetch(int *lon, int *lat, char *lon_s, char *lat_s, unsigned long *ts) +{ + char *bdy; + + memset(lon_s,0,16); + memset(lat_s,0,16); + + if (!url_download(url,(uint8_t*)download,sizeof(download))) + return false; + + bdy=strstr(download,"\r\n\r\n"); + if (!bdy) + return false; + bdy += 4; + + if (sscanf(bdy,fmt,lon_s,lat_s,ts)!=3) + return false; + + *lon = post_proc(lon_s); + *lat = post_proc(lat_s); + + return true; +} + +void satellite(int lon, int lat) +{ + unsigned char x = xpos[lon + 180] - CENTER_X; + unsigned char y = ypos[lat + 180] - CENTER_Y; + unsigned char i,j; + int8_t b; + + for (i=0;i<8;i++) + { + b=_satellite[i]; + for (j=0;j<16;j+=2) + { + if (b < 0) + tgi_setcolor(TGI_COLOR_WHITE2); + else + tgi_setcolor(TGI_COLOR_BLACK2); + tgi_setpixel(x+j,y+i); + tgi_setpixel(x+j+1,y+i); + b <<= 1; + } + } +} diff --git a/examples/30-dnd5eapi-cc65/iss-tracker/apple2/src/apple2.ip65/satellite.h b/examples/30-dnd5eapi-cc65/iss-tracker/apple2/src/apple2.ip65/satellite.h new file mode 100644 index 0000000..c8293ea --- /dev/null +++ b/examples/30-dnd5eapi-cc65/iss-tracker/apple2/src/apple2.ip65/satellite.h @@ -0,0 +1,15 @@ +/** + * ISS Tracker + * + * @author Thomas Cherryhomes + * @email thom dot cherryhomes at gmail dot com + * @license gpl v. 3, see LICENSE.md for details. + */ + +#ifndef SATELLITE_H +#define SATELLITE_H + +void satellite(int lon, int lat); +bool satellite_fetch(int *lon, int *lat, char *lon_s, char *lat_s, unsigned long *ts); + +#endif /* SATELLITE_H */ diff --git a/examples/30-dnd5eapi-cc65/iss-tracker/apple2/src/apple2.ip65/sp.c b/examples/30-dnd5eapi-cc65/iss-tracker/apple2/src/apple2.ip65/sp.c new file mode 100644 index 0000000..e69de29 diff --git a/examples/30-dnd5eapi-cc65/iss-tracker/apple2/src/apple2idiot.c b/examples/30-dnd5eapi-cc65/iss-tracker/apple2/src/apple2idiot.c new file mode 100644 index 0000000..67c76c4 --- /dev/null +++ b/examples/30-dnd5eapi-cc65/iss-tracker/apple2/src/apple2idiot.c @@ -0,0 +1,87 @@ +#include +#include +#include +#include + +//#include "globals.h" +#include "apple2idiot.h" +//#include "../../../../arduino-lib/Apple2Idiot/A2I_commands.h" + +#define MAX_STR_LEN 250 + +#define CARD_ADDRESS 0xC200 +#define APPLE_COMMAND_ADDRESS 0xC201 +#define RAM_DATA_START_ADDRESS 0xC202 +#define ESP_TIMEOUT 5555 +#define ACK 6 // Acknowledge +#define APP_ID = 202 + +unsigned int read_byte(unsigned int address) { + unsigned int b = 0; + b = PEEK(address); + //printf("read_data(%u)", address); + return b; +} + +unsigned char write_byte(unsigned int address, unsigned char byte_to_write) { + //printf("%u <- %d, [%c]\n", address, byte_to_write, byte_to_write); + POKE(address, byte_to_write); +} + +unsigned char write_byte_wait_for_ack(unsigned int address, unsigned char byte_to_write) { + unsigned char received_esp_response = 0; + int timeout_count = 0; + unsigned char timeout_happened = 0; + int delay_count = 0; + unsigned char read_char; + write_byte(address, byte_to_write); + while ((received_esp_response==0) || (timeout_happened==0)) { + timeout_count++; + if (timeout_count > ESP_TIMEOUT) { + timeout_happened = 1; + return 0; + } + //read_char = read_byte(ESP_COMMAND_ADDRESS); + read_char = read_byte(APPLE_COMMAND_ADDRESS); + if (read_char == ACK) { + received_esp_response = 1; + return 1; + } + for (delay_count=0; delay_count < 1111; ++delay_count) { + // do nothing + } + } +} + +unsigned char* write_string_to_ram(unsigned int address, char* string_to_send) { + unsigned char i; + unsigned char size = strlen(string_to_send); + //gotoxy(0,2); + if (string_to_send[size-1] == '\n') { + string_to_send[size-1] = '\0'; + } + //printf("%u (%s)\n", address, string_to_send); + for (i=0; i +#include +#include +#include +#include "map.h" +#include "satellite.h" +#include "osd.h" +//#include "sp.h" +#include "apple2idiot.h" + +#define CARD_ADDRESS 0xC200 +#define APPLE_COMMAND_ADDRESS 0xC201 +#define RAM_DATA_START_ADDRESS 0xC202 +#define ESP_TIMEOUT 5555 +#define ACK 6 // Acknowledge +#define APP_ID = 202 + +//unsigned char net; +char lon_s[16], lat_s[16]; +int lat, lon; +long timer; +unsigned long ts; + +void main(void) +{ + //sp_init(); + //net = sp_find_network(); + tgi_install(tgi_static_stddrv); + tgi_init(); + tgi_apple2_mix(true); + tgi_clear(); + + write_byte(APPLE_COMMAND_ADDRESS, 202); + + while (1) + { + timer=524088; + clrscr(); + satellite_fetch(&lon,&lat,&lon_s,&lat_s,&ts); + map(); + osd(lon_s,lat_s,ts); + satellite(lon,lat); + + while (timer>0) + { + if (kbhit()) + switch(cgetc()) + { + case 0x1b: + return; + case 0x0D: + case 0x0A: + timer=0; + break; + default: + break; + } + + timer--; + } + } +} diff --git a/examples/30-dnd5eapi-cc65/iss-tracker/apple2/src/map.c b/examples/30-dnd5eapi-cc65/iss-tracker/apple2/src/map.c new file mode 100644 index 0000000..a677cea --- /dev/null +++ b/examples/30-dnd5eapi-cc65/iss-tracker/apple2/src/map.c @@ -0,0 +1,31 @@ +/** + * ISS Tracker + * + * @author Thomas Cherryhomes + * @email thom dot cherryhomes at gmail dot com + * @license gpl v. 3, see LICENSE.md for details. + */ +#include +#include <6502.h> +#include +#include +#include +#include +#include +#include +#include + +void map(void) +{ + int f = open("MAP.HGR",O_RDONLY); + + if (f==-1) + { + perror("map open"); + cgetc(); + exit(1); + } + + read(f,(void *)0x2000,0x2000); + close(f); +} diff --git a/examples/30-dnd5eapi-cc65/iss-tracker/apple2/src/map.h b/examples/30-dnd5eapi-cc65/iss-tracker/apple2/src/map.h new file mode 100644 index 0000000..d176ef9 --- /dev/null +++ b/examples/30-dnd5eapi-cc65/iss-tracker/apple2/src/map.h @@ -0,0 +1,14 @@ +/** + * ISS Tracker + * + * @author Thomas Cherryhomes + * @email thom dot cherryhomes at gmail dot com + * @license gpl v. 3, see LICENSE.md for details. + */ + +#ifndef MAP_H +#define MAP_H + +void map(void); + +#endif /* MAP_H */ diff --git a/examples/30-dnd5eapi-cc65/iss-tracker/apple2/src/osd.c b/examples/30-dnd5eapi-cc65/iss-tracker/apple2/src/osd.c new file mode 100644 index 0000000..32d1c62 --- /dev/null +++ b/examples/30-dnd5eapi-cc65/iss-tracker/apple2/src/osd.c @@ -0,0 +1,18 @@ +/** + * ISS Tracker + * + * @author Thomas Cherryhomes + * @email thom dot cherryhomes at gmail dot com + * @license gpl v. 3, see LICENSE.md for details. + */ + +#include +#include +#include + +void osd(char *lon, char *lat,unsigned long ts) +{ + cprintf(" ** CURRENT ISS POSITION **\r\n"); + cprintf(" LON: %-14s LAT: %-14s",lon,lat); + cprintf(" AS OF: %s",ctime(&ts)); +} diff --git a/examples/30-dnd5eapi-cc65/iss-tracker/apple2/src/osd.h b/examples/30-dnd5eapi-cc65/iss-tracker/apple2/src/osd.h new file mode 100644 index 0000000..183d57c --- /dev/null +++ b/examples/30-dnd5eapi-cc65/iss-tracker/apple2/src/osd.h @@ -0,0 +1,14 @@ +/** + * ISS Tracker + * + * @author Thomas Cherryhomes + * @email thom dot cherryhomes at gmail dot com + * @license gpl v. 3, see LICENSE.md for details. + */ + +#ifndef OSD_H +#define OSD_H + +void osd(char *lon, char *lat, unsigned long ts); + +#endif /* OSD_H */ diff --git a/examples/30-dnd5eapi-cc65/iss-tracker/apple2/src/satellite.c b/examples/30-dnd5eapi-cc65/iss-tracker/apple2/src/satellite.c new file mode 100644 index 0000000..28f086e --- /dev/null +++ b/examples/30-dnd5eapi-cc65/iss-tracker/apple2/src/satellite.c @@ -0,0 +1,116 @@ +/** + * ISS Tracker + * + * @author Thomas Cherryhomes + * @email thom dot cherryhomes at gmail dot com + * @license gpl v. 3, see LICENSE.md for details. + */ + +#define CENTER_X 4 +#define CENTER_Y 4 + +#define APPLE_COMMAND_ADDRESS 0xC201 +#define RAM_DATA_START_ADDRESS 0xC202 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "satellite.h" +#include "apple2idiot.h" +//#include "sp.h" + +//extern unsigned char net; // Network device as found by sp_find_network() + +const unsigned char _satellite[8] = { + 0x20, /* ..X.|.... */ + 0x50, /* .X.X|.... */ + 0xA4, /* X.X.|.X.. */ + 0x58, /* .X.X|X... */ + 0x1A, /* ...X|X.X. */ + 0x05, /* ....|.X.X */ + 0x0A, /* ....|X.X. */ + 0x04, /* ....|.X.. */ +}; + +// TODO: These tables are broken, recalculate them! + +unsigned char xpos[360] = + { 0,0,1,2,3,3,4,5,6,7,7,8,9,10,10,11,12,13,14,14,15,16,17,17,18,19,20,21,21,22,23,24,24,25,26,27,28,28,29,30,31,31,32,33,34,35,35,36,37,38,38,39,40,41,42,42,43,44,45,45,46,47,48,49,49,50,51,52,52,53,54,55,56,56,57,58,59,59,60,61,62,63,63,64,65,66,66,67,68,69,70,70,71,72,73,73,74,75,76,77,77,78,79,80,80,81,82,83,84,84,85,86,87,87,88,89,90,91,91,92,93,94,94,95,96,97,98,98,99,100,101,101,102,103,104,105,105,106,107,108,108,109,110,111,112,112,113,114,115,115,116,117,118,119,119,120,121,122,122,123,124,125,126,126,127,128,129,129,130,131,132,133,133,134,135,136,136,137,138,139,140,140,141,142,143,143,144,145,146,147,147,148,149,150,150,151,152,153,154,154,155,156,157,157,158,159,160,161,161,162,163,164,164,165,166,167,168,168,169,170,171,171,172,173,174,175,175,176,177,178,178,179,180,181,182,182,183,184,185,185,186,187,188,189,189,190,191,192,192,193,194,195,196,196,197,198,199,199,200,201,202,203,203,204,205,206,206,207,208,209,210,210,211,212,213,213,214,215,216,217,217,218,219,220,220,221,222,223,224,224,225,226,227,227,228,229,230,231,231,232,233,234,234,235,236,237,238,238,239,240,241,241,242,243,244,245,245,246,247,248,248,249,250,251,252,252,253,254,255,255,0,1,2,3,3,4,5,6,6,7,8,9,10,10,11,12,13,13,14,15,16,17,17,18,19,20,20,21,22,23, }; + +unsigned char ypos[360] = + { 0,0,0,1,1,2,2,3,3,4,4,4,5,5,6,6,7,7,8,8,8,9,9,10,10,11,11,12,12,12,13,13,14,14,15,15,16,16,16,17,17,18,18,19,19,20,20,20,21,21,22,22,23,23,24,24,24,25,25,26,26,27,27,28,28,28,29,29,30,30,31,31,32,32,32,33,33,34,34,35,35,36,36,36,37,37,38,38,39,39,40,40,40,41,41,42,42,43,43,44,44,44,45,45,46,46,47,47,48,48,48,49,49,50,50,51,51,52,52,52,53,53,54,54,55,55,56,56,56,57,57,58,58,59,59,60,60,60,61,61,62,62,63,63,64,64,64,65,65,66,66,67,67,68,68,68,69,69,70,70,71,71,72,72,72,73,73,74,74,75,75,76,76,76,77,77,78,78,79,79,80,80,80,81,81,82,82,83,83,84,84,84,85,85,86,86,87,87,88,88,88,89,89,90,90,91,91,92,92,92,93,93,94,94,95,95,96,96,96,97,97,98,98,99,99,100,100,100,101,101,102,102,103,103,104,104,104,105,105,106,106,107,107,108,108,108,109,109,110,110,111,111,112,112,112,113,113,114,114,115,115,116,116,116,117,117,118,118,119,119,120,120,120,121,121,122,122,123,123,124,124,124,125,125,126,126,127,127,128,128,128,129,129,130,130,131,131,132,132,132,133,133,134,134,135,135,136,136,136,137,137,138,138,139,139,140,140,140,141,141,142,142,143,143,144,144,144,145,145,146,146,147,147,148,148,148,149,149,150,150,151,151,152,152,152,153,153,154,154,155,155,156,156,156,157,157,158,158,159,159, }; + +const char url[]="N:HTTP://api.open-notify.org/iss-now.json"; +const char longitude_query[]="/iss_position/longitude"; +const char latitude_query[]="/iss_position/latitude"; +const char timestamp_query[]="/timestamp"; + +int post_proc(char *s) +{ + char *c = strchr(s,'"'); + *c = 0; + return atoi(s); +} + +bool satellite_fetch(int *lon, int *lat, char *lon_s, char *lat_s, unsigned long *ts) +{ + //unsigned short len; + char ts_s[16]; + unsigned int address_offset = 0; + + //gotoxy(0,0); + + memset(lon_s,0,16); + memset(lat_s,0,16); + + write_byte_wait_for_ack(APPLE_COMMAND_ADDRESS, 20); + + address_offset = 0; + read_string_from_ram(RAM_DATA_START_ADDRESS + address_offset, lat_s, 9); + address_offset += strlen(lat_s) + 1; + read_string_from_ram(RAM_DATA_START_ADDRESS + address_offset, lon_s, 9); + address_offset += strlen(lon_s) + 1; + read_string_from_ram(RAM_DATA_START_ADDRESS + address_offset, ts_s, 9); + + *ts=atol(ts_s); + + //clrscr(); + //cprintf(" ** CURRENT ISS POSITION **\r\n"); + //cprintf(" LON: %-14s LAT: %-14s",lon_s,lat_s); + //cprintf(" TS: %-14s",ts_s); + //cprintf(" AS OF: %s",ctime(ts)); + + *lon=atoi(lon_s); + *lat=atoi(lat_s); + *ts=atol(ts_s); + + return true; // todo come back here and add error handling. +} + +void satellite(int lon, int lat) +{ + unsigned char x = xpos[lon + 180] - CENTER_X; + unsigned char y = ypos[lat + 180] - CENTER_Y; + unsigned char i,j; + int8_t b; + for (i=0;i<8;i++) + { + b=_satellite[i]; + for (j=0;j<16;j+=2) + { + if (b < 0) + tgi_setcolor(TGI_COLOR_WHITE2); + else + tgi_setcolor(TGI_COLOR_BLACK2); + tgi_setpixel(x+j,y+i); + tgi_setpixel(x+j+1,y+i); + b <<= 1; + } + } +} diff --git a/examples/30-dnd5eapi-cc65/iss-tracker/apple2/src/satellite.h b/examples/30-dnd5eapi-cc65/iss-tracker/apple2/src/satellite.h new file mode 100644 index 0000000..c8293ea --- /dev/null +++ b/examples/30-dnd5eapi-cc65/iss-tracker/apple2/src/satellite.h @@ -0,0 +1,15 @@ +/** + * ISS Tracker + * + * @author Thomas Cherryhomes + * @email thom dot cherryhomes at gmail dot com + * @license gpl v. 3, see LICENSE.md for details. + */ + +#ifndef SATELLITE_H +#define SATELLITE_H + +void satellite(int lon, int lat); +bool satellite_fetch(int *lon, int *lat, char *lon_s, char *lat_s, unsigned long *ts); + +#endif /* SATELLITE_H */ diff --git a/examples/30-dnd5eapi-cc65/iss-tracker/apple2/src/sp.c b/examples/30-dnd5eapi-cc65/iss-tracker/apple2/src/sp.c new file mode 100644 index 0000000..7878881 --- /dev/null +++ b/examples/30-dnd5eapi-cc65/iss-tracker/apple2/src/sp.c @@ -0,0 +1,365 @@ +#ifdef BUILD_APPLE2 +/** + * FujiNet CONFIG for #Apple2 + * + * SmartPort MLI Routines + */ + +#ifdef __INTELLISENSE__ +// 18, expect closing parenthses - needed to use cc65 inline asm command with agruments. + #pragma diag_suppress 18 +#endif + + +#include "sp.h" +#include +#include +#include +#include + +#define SP_CMD_STATUS 0 +#define SP_CMD_CONTROL 4 +#define SP_CMD_OPEN 6 +#define SP_CMD_CLOSE 7 +#define SP_CMD_READ 8 +#define SP_CMD_WRITE 9 +#define SP_STATUS_PARAM_COUNT 3 +#define SP_CONTROL_PARAM_COUNT 3 +#define SP_OPEN_PARAM_COUNT 1 +#define SP_CLOSE_PARAM_COUNT 1 +#define SP_READ_PARAM_COUNT 4 +#define SP_WRITE_PARAM_COUNT 4 + +// extern globals: +uint8_t sp_payload[1024]; +uint16_t sp_count; +uint8_t sp_dest; +uint16_t sp_dispatch; +uint8_t sp_error; + +static uint8_t sp_cmdlist[10]; +static uint8_t sp_cmdlist_low, sp_cmdlist_high; +static uint8_t sp_err, sp_rtn_low, sp_rtn_high; + +int8_t sp_status(uint8_t dest, uint8_t statcode) +{ + sp_error = 0; + // build the command list + sp_cmdlist[0] = SP_STATUS_PARAM_COUNT; + sp_cmdlist[1] = dest; // set before calling sp_status(); + sp_cmdlist[2] = (uint8_t)((uint16_t)&sp_payload & 0x00FF); + sp_cmdlist[3] = (uint8_t)((uint16_t)&sp_payload >> 8) & 0xFF; + sp_cmdlist[4] = statcode; + + sp_cmdlist_low = (uint8_t)((uint16_t)&sp_cmdlist & 0x00FF); + sp_cmdlist_high = (uint8_t)((uint16_t)&sp_cmdlist >> 8) & 0xFF; + + + // store cmd list + __asm__ volatile ("lda #%b", SP_CMD_STATUS); + __asm__ volatile ("sta %g", spCmd); // store status command # + __asm__ volatile ("lda %v", sp_cmdlist_low); + __asm__ volatile ("sta %g", spCmdListLow); // store status command # + __asm__ volatile ("lda %v", sp_cmdlist_high); + __asm__ volatile ("sta %g", spCmdListHigh); // store status command # + + __asm__ volatile ("jsr $C50D"); // to do - find SP entry point using algorithm from firmware reference +spCmd: + __asm__ volatile ("nop"); +spCmdListLow: + __asm__ volatile ("nop"); +spCmdListHigh: + __asm__ volatile ("nop"); + __asm__ volatile ("stx %v", sp_rtn_low); + __asm__ volatile ("sty %v", sp_rtn_high); + __asm__ volatile ("sta %v", sp_err); + + sp_count = ((uint16_t)sp_rtn_high << 8) | (uint16_t)sp_rtn_low; + sp_error = sp_err; + return sp_err; +} + +int8_t sp_control(uint8_t dest, uint8_t ctrlcode) +{ + sp_error = 0; + // sp_dest = 5; // need to search + // build the command list + sp_cmdlist[0] = SP_CONTROL_PARAM_COUNT; + sp_cmdlist[1] = dest; // set before calling sp_status(); + sp_cmdlist[2] = (uint8_t)((uint16_t)&sp_payload & 0x00FF); + sp_cmdlist[3] = (uint8_t)((uint16_t)&sp_payload >> 8) & 0xFF; + sp_cmdlist[4] = ctrlcode; + + sp_cmdlist_low = (uint8_t)((uint16_t)&sp_cmdlist & 0x00FF); + sp_cmdlist_high = (uint8_t)((uint16_t)&sp_cmdlist >> 8) & 0xFF; + + // store cmd list + __asm__ volatile ("lda #%b", SP_CMD_CONTROL); + __asm__ volatile ("sta %g", spCmd); // store status command # + __asm__ volatile ("lda %v", sp_cmdlist_low); + __asm__ volatile ("sta %g", spCmdListLow); // store status command # + __asm__ volatile ("lda %v", sp_cmdlist_high); + __asm__ volatile ("sta %g", spCmdListHigh); // store status command # + + __asm__ volatile ("jsr $C50D"); // to do - find entry point and used it instead of hardcoded address +spCmd: + __asm__ volatile ("nop"); +spCmdListLow: + __asm__ volatile ("nop"); +spCmdListHigh: + __asm__ volatile ("nop"); + __asm__ volatile ("sta %v", sp_err); + sp_error = sp_err; + return sp_err; +} + +int8_t sp_open(uint8_t dest) +{ + sp_error = 0; + // sp_dest = 5; // need to search + // build the command list + sp_cmdlist[0] = SP_OPEN_PARAM_COUNT; + sp_cmdlist[1] = dest; // set before calling sp_status(); + + sp_cmdlist_low = (uint8_t)((uint16_t)&sp_cmdlist & 0x00FF); + sp_cmdlist_high = (uint8_t)((uint16_t)&sp_cmdlist >> 8) & 0xFF; + + // store cmd list + __asm__ volatile ("lda #%b", SP_CMD_OPEN); + __asm__ volatile ("sta %g", spCmd); // store status command # + __asm__ volatile ("lda %v", sp_cmdlist_low); + __asm__ volatile ("sta %g", spCmdListLow); // store status command # + __asm__ volatile ("lda %v", sp_cmdlist_high); + __asm__ volatile ("sta %g", spCmdListHigh); // store status command # + + __asm__ volatile ("jsr $C50D"); // to do - find entry point and used it instead of hardcoded address +spCmd: + __asm__ volatile ("nop"); +spCmdListLow: + __asm__ volatile ("nop"); +spCmdListHigh: + __asm__ volatile ("nop"); + __asm__ volatile ("sta %v", sp_err); + sp_error = sp_err; + return sp_err; +} + +int8_t sp_close(uint8_t dest) +{ + sp_error = 0; + // sp_dest = 5; // need to search + // build the command list + sp_cmdlist[0] = SP_CLOSE_PARAM_COUNT; + sp_cmdlist[1] = dest; // set before calling sp_status(); + + sp_cmdlist_low = (uint8_t)((uint16_t)&sp_cmdlist & 0x00FF); + sp_cmdlist_high = (uint8_t)((uint16_t)&sp_cmdlist >> 8) & 0xFF; + + // store cmd list + __asm__ volatile ("lda #%b", SP_CMD_CLOSE); + __asm__ volatile ("sta %g", spCmd); // store status command # + __asm__ volatile ("lda %v", sp_cmdlist_low); + __asm__ volatile ("sta %g", spCmdListLow); // store status command # + __asm__ volatile ("lda %v", sp_cmdlist_high); + __asm__ volatile ("sta %g", spCmdListHigh); // store status command # + + __asm__ volatile ("jsr $C50D"); // to do - find entry point and used it instead of hardcoded address +spCmd: + __asm__ volatile ("nop"); +spCmdListLow: + __asm__ volatile ("nop"); +spCmdListHigh: + __asm__ volatile ("nop"); + __asm__ volatile ("sta %v", sp_err); + sp_error = sp_err; + return sp_err; +} + +int8_t sp_read(uint8_t dest, uint16_t len) +{ + sp_error = 0; + // sp_dest = 5; // need to search + // build the command list + sp_cmdlist[0] = SP_READ_PARAM_COUNT; + sp_cmdlist[1] = dest; // set before calling sp_status(); + sp_cmdlist[2] = (uint8_t)((uint16_t)&sp_payload & 0x00FF); + sp_cmdlist[3] = (uint8_t)((uint16_t)&sp_payload >> 8) & 0xFF; + sp_cmdlist[4] = len & 0xFF; + sp_cmdlist[5] = len >> 8; + + sp_cmdlist_low = (uint8_t)((uint16_t)&sp_cmdlist & 0x00FF); + sp_cmdlist_high = (uint8_t)((uint16_t)&sp_cmdlist >> 8) & 0xFF; + + // store cmd list + __asm__ volatile ("lda #%b", SP_CMD_READ); + __asm__ volatile ("sta %g", spCmd); // store status command # + __asm__ volatile ("lda %v", sp_cmdlist_low); + __asm__ volatile ("sta %g", spCmdListLow); // store status command # + __asm__ volatile ("lda %v", sp_cmdlist_high); + __asm__ volatile ("sta %g", spCmdListHigh); // store status command # + + __asm__ volatile ("jsr $C50D"); // to do - find entry point and used it instead of hardcoded address +spCmd: + __asm__ volatile ("nop"); +spCmdListLow: + __asm__ volatile ("nop"); +spCmdListHigh: + __asm__ volatile ("nop"); + __asm__ volatile ("sta %v", sp_err); + sp_error = sp_err; + return sp_err; +} + +int8_t sp_write(uint8_t dest, uint16_t len) +{ + sp_error = 0; + // build the command list + sp_cmdlist[0] = SP_READ_PARAM_COUNT; + sp_cmdlist[1] = dest; // set before calling sp_status(); + sp_cmdlist[2] = (uint8_t)((uint16_t)&sp_payload & 0x00FF); + sp_cmdlist[3] = (uint8_t)((uint16_t)&sp_payload >> 8) & 0xFF; + sp_cmdlist[4] = len & 0xFF; + sp_cmdlist[5] = len >> 8; + + sp_cmdlist_low = (uint8_t)((uint16_t)&sp_cmdlist & 0x00FF); + sp_cmdlist_high = (uint8_t)((uint16_t)&sp_cmdlist >> 8) & 0xFF; + + // store cmd list + __asm__ volatile ("lda #%b", SP_CMD_WRITE); + __asm__ volatile ("sta %g", spCmd); // store status command # + __asm__ volatile ("lda %v", sp_cmdlist_low); + __asm__ volatile ("sta %g", spCmdListLow); // store status command # + __asm__ volatile ("lda %v", sp_cmdlist_high); + __asm__ volatile ("sta %g", spCmdListHigh); // store status command # + + __asm__ volatile ("jsr $C50D"); // to do - find entry point and used it instead of hardcoded address +spCmd: + __asm__ volatile ("nop"); +spCmdListLow: + __asm__ volatile ("nop"); +spCmdListHigh: + __asm__ volatile ("nop"); + __asm__ volatile ("sta %v", sp_err); + sp_error = sp_err; + return sp_err; +} + +int8_t sp_find_fuji() +{ + // const char fuji[9] = "THE_FUJI"; + const char fuji[14] = "FUJINET_DISK_0"; + const uint8_t fuji_len = sizeof(fuji); + int8_t err, num, i, j; + + err = sp_status(0x00, 0x00); // get number of devices + if (err) + return -err; + num = sp_payload[0]; + num++; + for (i = 1; i < num; i++) + { + //do + err = sp_status(i, 0x03); // get DIB + //while (err); + if (sp_payload[4] == fuji_len) + { + for (j = 0; j < fuji_len; j++) + if (fuji[j]!=sp_payload[5+j]) + return 0; + sp_dest = i; // store the fuji unit # + return i; + } + } + return 0; +} + +int8_t sp_find_network() +{ + const char net[7] = "NETWORK"; + const uint8_t net_len = sizeof(net); + int8_t err, num, i, j; + + err = sp_status(0x00, 0x00); // get number of devices + + if (err) + return -err; + + num = sp_payload[0]; + num+=2; + + for (i = 1; i < num; i++) + { + err = sp_status(i, 0x03); // get DIB + + if (sp_payload[4] == net_len) + { + for (j = 0; j < net_len; j++) + if (net[j]!=sp_payload[5+j]) + return 0; + + return i; + } + } + printf("NET NOT FOUND"); + cgetc(); + return 0; +} + +/** + * Apple // SmartPort routines for CC65 + * + * @author Thomas Cherryhomes + * @email thom dot cherryhomes at gmail dot com + * + */ + +/** + * Check for SmartPort presence + * @return slot #, or 0xFF if not present. + */ +uint8_t sp_find_slot(void) +{ + uint8_t s=0; + + for (s=7; s-- > 0;) + { + uint16_t a = 0xc000 + (s * 0x100); + if ((PEEK(a+1) == 0x20) && + (PEEK(a+3) == 0x00) && + (PEEK(a+5) == 0x03) && + (PEEK(a+7) == 0x00)) + return s; + } + + // Did not find. + return 0; +} + +/** + * Return dispatch address for Smartport slot. + * @param s Slot # (1-7) + * @return smartport dispatch address + */ +uint16_t sp_dispatch_address(uint8_t slot) +{ + uint16_t a = (slot * 0x100) + 0xC000; + uint8_t j = PEEK(a+0xFF); + + return a + j + 3; +} + +void sp_init(void) +{ + uint8_t slot, f; + slot = sp_find_slot(); + if (slot) + sp_dispatch = sp_dispatch_address(slot); + else + cprintf("No SmartPort Firmware Found!"); + //sp_list_devs(); + f = sp_find_fuji(); + if (f < 1) + cprintf("FujiNet Not Found!"); +} + +#endif /* BUILD_APPLE2 */ diff --git a/examples/30-dnd5eapi-cc65/iss-tracker/apple2/src/sp.h b/examples/30-dnd5eapi-cc65/iss-tracker/apple2/src/sp.h new file mode 100644 index 0000000..7540771 --- /dev/null +++ b/examples/30-dnd5eapi-cc65/iss-tracker/apple2/src/sp.h @@ -0,0 +1,30 @@ +/** + * FujiNet CONFIG for #Apple2 + * + * SmartPort MLI Routines + */ + +#ifndef SP_H +#define SP_H + +#include + +extern uint8_t sp_payload[1024]; +extern uint16_t sp_count, sp_dispatch; +extern uint8_t sp_dest; +extern uint8_t sp_error; + +int8_t sp_status(uint8_t dest, uint8_t statcode); +int8_t sp_control(uint8_t dest, uint8_t ctrlcode); +int8_t sp_open(uint8_t dest); +int8_t sp_close(uint8_t dest); +int8_t sp_read(uint8_t dest, uint16_t len); +int8_t sp_write(uint8_t dest, uint16_t len); +int8_t sp_find_fuji(void); +int8_t sp_find_network(void); +uint8_t sp_find_slot(void); +uint16_t sp_dispatch_address(uint8_t slot); +void sp_init(void); + +#endif /* SP_H */ + diff --git a/examples/30-dnd5eapi-cc65/iss_commands.h b/examples/30-dnd5eapi-cc65/iss_commands.h new file mode 100644 index 0000000..584b77c --- /dev/null +++ b/examples/30-dnd5eapi-cc65/iss_commands.h @@ -0,0 +1,9 @@ +#ifndef A2I_ISS_COMMANDS_H +#define A2I_ISS_COMMANDS_H + +/* Apple II <-> ESP Commands */ +#define COMMAND_GET_ISS 20 + +/* Responses */ + +#endif diff --git a/examples/30-dnd5eapi-cc65/json_examples/monster_ab.json b/examples/30-dnd5eapi-cc65/json_examples/monster_ab.json new file mode 100644 index 0000000..008aebe --- /dev/null +++ b/examples/30-dnd5eapi-cc65/json_examples/monster_ab.json @@ -0,0 +1 @@ +{"error":"Not found"} \ No newline at end of file diff --git a/examples/30-dnd5eapi-cc65/json_examples/monster_aboleth.json b/examples/30-dnd5eapi-cc65/json_examples/monster_aboleth.json new file mode 100644 index 0000000..ae5639c --- /dev/null +++ b/examples/30-dnd5eapi-cc65/json_examples/monster_aboleth.json @@ -0,0 +1 @@ +{"index":"aboleth","name":"Aboleth","size":"Large","type":"aberration","alignment":"lawful evil","armor_class":17,"hit_points":135,"hit_dice":"18d10","speed":{"walk":"10 ft.","swim":"40 ft."},"strength":21,"dexterity":9,"constitution":15,"intelligence":18,"wisdom":15,"charisma":18,"proficiencies":[{"value":6,"proficiency":{"index":"saving-throw-con","name":"Saving Throw: CON","url":"/api/proficiencies/saving-throw-con"}},{"value":8,"proficiency":{"index":"saving-throw-int","name":"Saving Throw: INT","url":"/api/proficiencies/saving-throw-int"}},{"value":6,"proficiency":{"index":"saving-throw-wis","name":"Saving Throw: WIS","url":"/api/proficiencies/saving-throw-wis"}},{"value":12,"proficiency":{"index":"skill-history","name":"Skill: History","url":"/api/proficiencies/skill-history"}},{"value":10,"proficiency":{"index":"skill-perception","name":"Skill: Perception","url":"/api/proficiencies/skill-perception"}}],"damage_vulnerabilities":[],"damage_resistances":[],"damage_immunities":[],"condition_immunities":[],"senses":{"darkvision":"120 ft.","passive_perception":20},"languages":"Deep Speech, telepathy 120 ft.","challenge_rating":10,"xp":5900,"special_abilities":[{"name":"Amphibious","desc":"The aboleth can breathe air and water."},{"name":"Mucous Cloud","desc":"While underwater, the aboleth is surrounded by transformative mucus. A creature that touches the aboleth or that hits it with a melee attack while within 5 ft. of it must make a DC 14 Constitution saving throw. On a failure, the creature is diseased for 1d4 hours. The diseased creature can breathe only underwater.","dc":{"dc_type":{"index":"con","name":"CON","url":"/api/ability-scores/con"},"dc_value":14,"success_type":"none"}},{"name":"Probing Telepathy","desc":"If a creature communicates telepathically with the aboleth, the aboleth learns the creature's greatest desires if the aboleth can see the creature."}],"actions":[{"name":"Multiattack","desc":"The aboleth makes three tentacle attacks.","options":{"choose":1,"from":[{"0":{"name":"Tentacle","count":3,"type":"melee"}}]}},{"name":"Tentacle","desc":"Melee Weapon Attack: +9 to hit, reach 10 ft., one target. Hit: 12 (2d6 + 5) bludgeoning damage. If the target is a creature, it must succeed on a DC 14 Constitution saving throw or become diseased. The disease has no effect for 1 minute and can be removed by any magic that cures disease. After 1 minute, the diseased creature's skin becomes translucent and slimy, the creature can't regain hit points unless it is underwater, and the disease can be removed only by heal or another disease-curing spell of 6th level or higher. When the creature is outside a body of water, it takes 6 (1d12) acid damage every 10 minutes unless moisture is applied to the skin before 10 minutes have passed.","attack_bonus":9,"dc":{"dc_type":{"index":"con","name":"CON","url":"/api/ability-scores/con"},"dc_value":14,"success_type":"none"},"damage":[{"damage_type":{"index":"bludgeoning","name":"Bludgeoning","url":"/api/damage-types/bludgeoning"},"damage_dice":"2d6+5"},{"damage_type":{"index":"acid","name":"Acid","url":"/api/damage-types/acid"},"damage_dice":"1d12"}]},{"name":"Tail","desc":"Melee Weapon Attack: +9 to hit, reach 10 ft. one target. Hit: 15 (3d6 + 5) bludgeoning damage.","attack_bonus":9,"damage":[{"damage_type":{"index":"bludgeoning","name":"Bludgeoning","url":"/api/damage-types/bludgeoning"},"damage_dice":"3d6+5"}]},{"name":"Enslave","desc":"The aboleth targets one creature it can see within 30 ft. of it. The target must succeed on a DC 14 Wisdom saving throw or be magically charmed by the aboleth until the aboleth dies or until it is on a different plane of existence from the target. The charmed target is under the aboleth's control and can't take reactions, and the aboleth and the target can communicate telepathically with each other over any distance.\nWhenever the charmed target takes damage, the target can repeat the saving throw. On a success, the effect ends. No more than once every 24 hours, the target can also repeat the saving throw when it is at least 1 mile away from the aboleth.","usage":{"type":"per day","times":3},"dc":{"dc_type":{"index":"wis","name":"WIS","url":"/api/ability-scores/wis"},"dc_value":14,"success_type":"none"}}],"legendary_actions":[{"name":"Detect","desc":"The aboleth makes a Wisdom (Perception) check."},{"name":"Tail Swipe","desc":"The aboleth makes one tail attack."},{"name":"Psychic Drain (Costs 2 Actions)","desc":"One creature charmed by the aboleth takes 10 (3d6) psychic damage, and the aboleth regains hit points equal to the damage the creature takes.","attack_bonus":0,"damage":[{"damage_type":{"index":"psychic","name":"Psychic","url":"/api/damage-types/psychic"},"damage_dice":"3d6"}]}],"url":"/api/monsters/aboleth"} \ No newline at end of file diff --git a/examples/30-dnd5eapi-cc65/oldino.foo b/examples/30-dnd5eapi-cc65/oldino.foo new file mode 100644 index 0000000..f3dc231 --- /dev/null +++ b/examples/30-dnd5eapi-cc65/oldino.foo @@ -0,0 +1,187 @@ +/* +Use this program with the Apple2idIOT card and the basic programs RRAM, WRAM and CMDROT to read/write and rot13 +a single string contained within the dual port ram on the card. +CA = 49664 +AA = CA + 1 +*/ + +// Load Wi-Fi library +#include +#include +#include +#include + +Apple2Idiot a2i = Apple2Idiot(); + +#define COMMAND_SET_COUNTRY 200 +#define COMMAND_SET_CITY 201 +#define COMMAND_FETCH_WEATHER 205 + +#include "credentials.h" + +//const char* wifi_ssid = "HotelMcCoy-Guest"; +//const char* wifi_password = "travelforall"; +char wifi_ssid[] = WIFI_SSID; // your network SSID (name) +char wifi_password[] = WIFI_PASSWORD; // your network password + +/*******************/ +/* Weather Service */ +/*******************/ + +const String weather_service_api_key= "0ab97bbbea58592d7c9d64067a34d2d0"; +const String weather_url = "http://api.openweathermap.org/data/2.5/weather?"; + +String country_code = "US"; +String city_name = "Tucson"; + +/*******************/ +/* Misc */ +/*******************/ + +const long readLoopInterval = 100; // millis +unsigned long lastReadLoopTime = 0; + +byte lastAppleCommand = 0; + +/*################################################ +# Setup # +################################################*/ + +void setup() { + Serial.println("Starting 15_fixed_essid_weather-cc65.ino after a quick delay so that we have time to connect a serial monitor after restart and see what's going on"); + delay(3000); + Serial.println("Starting 15_fixed_essid_weather-cc65.ino"); + + Serial.begin(115200); + + a2i.init(); + + Serial.println(""); + Serial.println("Starting wifi..."); + Serial.print(" connecting to: "); + Serial.println(wifi_ssid); + + WiFi.begin(wifi_ssid, wifi_password); + while (WiFi.status() != WL_CONNECTED) { + delay(600); + Serial.print("."); + } + + Serial.println(""); + Serial.println("WiFi connected successfully"); + Serial.print("Got IP: "); + Serial.println(WiFi.localIP()); //Show ESP32 IP on serial + + Serial.println("Setup done"); +} + +/*################################################ +# Functions # +################################################*/ + +byte fetch_weather() { + + byte result = 0; + HTTPClient http; + //const String request_url = weather_url + "q=Tucson,us&APPID=" + weather_service_api_key; + const String request_url = weather_url + "q=" + city_name + "," + country_code + "&APPID=" + WEATHER_ACCESS_TOKEN; + Serial.println(request_url); + http.begin(request_url); + int httpCode = http.GET(); //Make the request + delay(10); + + if (httpCode > 0) { //Check for the returning code + String payload = http.getString(); + //Serial.println(httpCode); + Serial.println("++++++++++++++++++++++++"); + Serial.println(payload); + Serial.println("++++++++++++++++++++++++"); + StaticJsonDocument<200> filter; + filter["weather"][0]["main"] = true; + filter["weather"][0]["description"] = true; + filter["main"]["humidity"] = true; + filter["main"]["temp"] = true; + filter["wind"]["speed"] = true; + filter["wind"]["deg"] = true; + StaticJsonDocument<400> doc; + DeserializationError error = deserializeJson(doc, payload, DeserializationOption::Filter(filter)); + //DeserializationError error = deserializeJson(doc, payload); + if (error) { + Serial.print(F("deserializeJson() failed: ")); + Serial.println(error.f_str()); + } else { + Serial.println("----------------------"); + serializeJsonPretty(doc, Serial); + Serial.println("----------------------"); + serializeJsonPretty(doc["wind"], Serial); + Serial.println("----------------------"); + String temp = doc["main"]["temp"]; + String humidity = doc["main"]["humidity"]; + String wind_speed = doc["wind"]["speed"]; + String wind_deg = doc["wind"]["deg"]; + String weather_description1 = doc["weather"][0]["main"]; + String weather_description2 = doc["weather"][0]["description"]; + int address_counter = a2i.write_string_to_shared_ram(temp, SHARED_RAM_START_ADDRESS); + address_counter = a2i.write_string_to_shared_ram(humidity, address_counter + 1); + address_counter = a2i.write_string_to_shared_ram(wind_speed, address_counter + 1); + address_counter = a2i.write_string_to_shared_ram(wind_deg, address_counter + 1); + address_counter = a2i.write_string_to_shared_ram(weather_description1, address_counter + 1); + address_counter = a2i.write_string_to_shared_ram(weather_description2, address_counter + 1); + } + result = ACK; + } + else { + Serial.println("Error on HTTP request"); + result = ERR; + } + + http.end(); //Free the resources + return result; +} + + +/*################################################ +# Main # +################################################*/ + +void loop() { + + if ((millis() - lastReadLoopTime) > readLoopInterval) { + byte command_byte = a2i.read_data(APPLE_COMMAND_ADDRESS); + if (command_byte == RAM_BUSY) { + Serial.println("Command Read: RAM BUSY"); + } + else if (command_byte != lastAppleCommand){ + byte result = 0; + Serial.print("Command Switch command_byte: "); + Serial.println(command_byte); + switch(command_byte) { + case COMMAND_SET_COUNTRY: + Serial.println("COMMAND_SET_COUNTRY"); + a2i.write_data(ESP_COMMAND_ADDRESS, ACK); // notify Apple IIe we are processing command byte + country_code = a2i.read_string_from_ram(SHARED_RAM_START_ADDRESS); + Serial.println("Received: ["+country_code+"]"); + a2i.write_data(APPLE_COMMAND_ADDRESS, ACK); + a2i.write_data(ESP_COMMAND_ADDRESS, EOT); // notify Apple IIe we are done processing command byte + break; + case COMMAND_SET_CITY: + Serial.println("COMMAND_SET_CITY"); + a2i.write_data(ESP_COMMAND_ADDRESS, ACK); // notify Apple IIe we are processing command byte + city_name = a2i.read_string_from_ram(SHARED_RAM_START_ADDRESS); + Serial.println("Received: ["+city_name+"]"); + a2i.write_data(APPLE_COMMAND_ADDRESS, ACK); + a2i.write_data(ESP_COMMAND_ADDRESS, EOT); // notify Apple IIe we are done processing command byte + break; + case COMMAND_FETCH_WEATHER: + Serial.println("COMMAND_FETCH_WEATHER"); + a2i.write_data(ESP_COMMAND_ADDRESS, ACK); // notify Apple IIe we are processing command byte + result = fetch_weather(); + a2i.write_data(APPLE_COMMAND_ADDRESS, ACK); + a2i.write_data(ESP_COMMAND_ADDRESS, result); // notify Apple IIe we are done processing command byte + break; + } + lastAppleCommand = command_byte; + } + lastReadLoopTime = millis(); + } +} diff --git a/examples/30-dnd5eapi-cc65/weather_commands.h b/examples/30-dnd5eapi-cc65/weather_commands.h new file mode 100644 index 0000000..7d83ede --- /dev/null +++ b/examples/30-dnd5eapi-cc65/weather_commands.h @@ -0,0 +1,13 @@ +#ifndef A2I_WEATHER_COMMANDS_H +#define A2I_WEATHER_COMMANDS_H + +/* Apple II <-> ESP Commands */ +#define COMMAND_SET_COUNTRY 20 +#define COMMAND_SET_CITY 21 +#define COMMAND_FETCH_WEATHER 25 + +/* Responses */ +//#define CHESS_INVALID_MOVE 123 +//#define CHESS_VALID_MOVE 124 + +#endif diff --git a/examples/README.md b/examples/README.md index c35d122..edf316d 100644 --- a/examples/README.md +++ b/examples/README.md @@ -1,7 +1,5 @@ # apple2idiot -A general purpose ESP32 IOT board for the Apple IIe -The best example at the moment is `weather/`. ## Potential requirements diff --git a/examples/cc65-Chess/.vscode/c_cpp_properties.json b/examples/defunct-explorations/cc65-Chess-2/.vscode/c_cpp_properties.json similarity index 100% rename from examples/cc65-Chess/.vscode/c_cpp_properties.json rename to examples/defunct-explorations/cc65-Chess-2/.vscode/c_cpp_properties.json diff --git a/examples/cc65-Chess/.vscode/settings.json b/examples/defunct-explorations/cc65-Chess-2/.vscode/settings.json similarity index 100% rename from examples/cc65-Chess/.vscode/settings.json rename to examples/defunct-explorations/cc65-Chess-2/.vscode/settings.json diff --git a/examples/cc65-Chess/.vscode/tasks.json b/examples/defunct-explorations/cc65-Chess-2/.vscode/tasks.json similarity index 100% rename from examples/cc65-Chess/.vscode/tasks.json rename to examples/defunct-explorations/cc65-Chess-2/.vscode/tasks.json diff --git a/examples/cc65-Chess/apple2-cc65/Makefile b/examples/defunct-explorations/cc65-Chess-2/apple2-cc65/Makefile similarity index 100% rename from examples/cc65-Chess/apple2-cc65/Makefile rename to examples/defunct-explorations/cc65-Chess-2/apple2-cc65/Makefile diff --git a/examples/cc65-Chess/apple2-cc65/Makefile-dsk.mk b/examples/defunct-explorations/cc65-Chess-2/apple2-cc65/Makefile-dsk.mk similarity index 100% rename from examples/cc65-Chess/apple2-cc65/Makefile-dsk.mk rename to examples/defunct-explorations/cc65-Chess-2/apple2-cc65/Makefile-dsk.mk diff --git a/examples/cc65-Chess/apple2-cc65/Makefile.options b/examples/defunct-explorations/cc65-Chess-2/apple2-cc65/Makefile.options similarity index 100% rename from examples/cc65-Chess/apple2-cc65/Makefile.options rename to examples/defunct-explorations/cc65-Chess-2/apple2-cc65/Makefile.options diff --git a/examples/cc65-Chess/apple2-cc65/apple2/template.dsk b/examples/defunct-explorations/cc65-Chess-2/apple2-cc65/apple2/template.dsk similarity index 100% rename from examples/cc65-Chess/apple2-cc65/apple2/template.dsk rename to examples/defunct-explorations/cc65-Chess-2/apple2-cc65/apple2/template.dsk diff --git a/examples/cc65-Chess/apple2-cc65/cc65-Chess.apple2 b/examples/defunct-explorations/cc65-Chess-2/apple2-cc65/cc65-Chess.apple2 similarity index 100% rename from examples/cc65-Chess/apple2-cc65/cc65-Chess.apple2 rename to examples/defunct-explorations/cc65-Chess-2/apple2-cc65/cc65-Chess.apple2 diff --git a/examples/cc65-Chess/apple2-cc65/chess.apple2 b/examples/defunct-explorations/cc65-Chess-2/apple2-cc65/chess.apple2 similarity index 100% rename from examples/cc65-Chess/apple2-cc65/chess.apple2 rename to examples/defunct-explorations/cc65-Chess-2/apple2-cc65/chess.apple2 diff --git a/examples/cc65-Chess/apple2-cc65/obj/apple2/apple2idiot.d b/examples/defunct-explorations/cc65-Chess-2/apple2-cc65/obj/apple2/apple2idiot.d similarity index 100% rename from examples/cc65-Chess/apple2-cc65/obj/apple2/apple2idiot.d rename to examples/defunct-explorations/cc65-Chess-2/apple2-cc65/obj/apple2/apple2idiot.d diff --git a/examples/cc65-Chess/apple2-cc65/obj/apple2/globals.d b/examples/defunct-explorations/cc65-Chess-2/apple2-cc65/obj/apple2/globals.d similarity index 100% rename from examples/cc65-Chess/apple2-cc65/obj/apple2/globals.d rename to examples/defunct-explorations/cc65-Chess-2/apple2-cc65/obj/apple2/globals.d diff --git a/examples/cc65-Chess/apple2-cc65/obj/apple2/hires.d b/examples/defunct-explorations/cc65-Chess-2/apple2-cc65/obj/apple2/hires.d similarity index 100% rename from examples/cc65-Chess/apple2-cc65/obj/apple2/hires.d rename to examples/defunct-explorations/cc65-Chess-2/apple2-cc65/obj/apple2/hires.d diff --git a/examples/cc65-Chess/apple2-cc65/obj/apple2/human.d b/examples/defunct-explorations/cc65-Chess-2/apple2-cc65/obj/apple2/human.d similarity index 100% rename from examples/cc65-Chess/apple2-cc65/obj/apple2/human.d rename to examples/defunct-explorations/cc65-Chess-2/apple2-cc65/obj/apple2/human.d diff --git a/examples/cc65-Chess/apple2-cc65/obj/apple2/main.d b/examples/defunct-explorations/cc65-Chess-2/apple2-cc65/obj/apple2/main.d similarity index 100% rename from examples/cc65-Chess/apple2-cc65/obj/apple2/main.d rename to examples/defunct-explorations/cc65-Chess-2/apple2-cc65/obj/apple2/main.d diff --git a/examples/cc65-Chess/apple2-cc65/obj/apple2/platA2.d b/examples/defunct-explorations/cc65-Chess-2/apple2-cc65/obj/apple2/platA2.d similarity index 100% rename from examples/cc65-Chess/apple2-cc65/obj/apple2/platA2.d rename to examples/defunct-explorations/cc65-Chess-2/apple2-cc65/obj/apple2/platA2.d diff --git a/examples/cc65-Chess/apple2-cc65/src/apple2/charset.bin b/examples/defunct-explorations/cc65-Chess-2/apple2-cc65/src/apple2/charset.bin similarity index 100% rename from examples/cc65-Chess/apple2-cc65/src/apple2/charset.bin rename to examples/defunct-explorations/cc65-Chess-2/apple2-cc65/src/apple2/charset.bin diff --git a/examples/cc65-Chess/apple2-cc65/src/apple2/genPieces.cpp b/examples/defunct-explorations/cc65-Chess-2/apple2-cc65/src/apple2/genPieces.cpp similarity index 100% rename from examples/cc65-Chess/apple2-cc65/src/apple2/genPieces.cpp rename to examples/defunct-explorations/cc65-Chess-2/apple2-cc65/src/apple2/genPieces.cpp diff --git a/examples/cc65-Chess/apple2-cc65/src/apple2/hires.s b/examples/defunct-explorations/cc65-Chess-2/apple2-cc65/src/apple2/hires.s similarity index 100% rename from examples/cc65-Chess/apple2-cc65/src/apple2/hires.s rename to examples/defunct-explorations/cc65-Chess-2/apple2-cc65/src/apple2/hires.s diff --git a/examples/cc65-Chess/apple2-cc65/src/apple2/pieces.bin b/examples/defunct-explorations/cc65-Chess-2/apple2-cc65/src/apple2/pieces.bin similarity index 100% rename from examples/cc65-Chess/apple2-cc65/src/apple2/pieces.bin rename to examples/defunct-explorations/cc65-Chess-2/apple2-cc65/src/apple2/pieces.bin diff --git a/examples/cc65-Chess/apple2-cc65/src/apple2/platA2.c b/examples/defunct-explorations/cc65-Chess-2/apple2-cc65/src/apple2/platA2.c similarity index 100% rename from examples/cc65-Chess/apple2-cc65/src/apple2/platA2.c rename to examples/defunct-explorations/cc65-Chess-2/apple2-cc65/src/apple2/platA2.c diff --git a/examples/cc65-Chess/apple2-cc65/src/apple2idiot.c b/examples/defunct-explorations/cc65-Chess-2/apple2-cc65/src/apple2idiot.c similarity index 100% rename from examples/cc65-Chess/apple2-cc65/src/apple2idiot.c rename to examples/defunct-explorations/cc65-Chess-2/apple2-cc65/src/apple2idiot.c diff --git a/examples/cc65-Chess/apple2-cc65/src/apple2idiot.h b/examples/defunct-explorations/cc65-Chess-2/apple2-cc65/src/apple2idiot.h similarity index 100% rename from examples/cc65-Chess/apple2-cc65/src/apple2idiot.h rename to examples/defunct-explorations/cc65-Chess-2/apple2-cc65/src/apple2idiot.h diff --git a/examples/cc65-Chess/apple2-cc65/src/chess b/examples/defunct-explorations/cc65-Chess-2/apple2-cc65/src/chess similarity index 100% rename from examples/cc65-Chess/apple2-cc65/src/chess rename to examples/defunct-explorations/cc65-Chess-2/apple2-cc65/src/chess diff --git a/examples/cc65-Chess/apple2-cc65/src/globals.c b/examples/defunct-explorations/cc65-Chess-2/apple2-cc65/src/globals.c similarity index 100% rename from examples/cc65-Chess/apple2-cc65/src/globals.c rename to examples/defunct-explorations/cc65-Chess-2/apple2-cc65/src/globals.c diff --git a/examples/cc65-Chess/apple2-cc65/src/globals.h b/examples/defunct-explorations/cc65-Chess-2/apple2-cc65/src/globals.h similarity index 100% rename from examples/cc65-Chess/apple2-cc65/src/globals.h rename to examples/defunct-explorations/cc65-Chess-2/apple2-cc65/src/globals.h diff --git a/examples/cc65-Chess/apple2-cc65/src/human.c b/examples/defunct-explorations/cc65-Chess-2/apple2-cc65/src/human.c similarity index 100% rename from examples/cc65-Chess/apple2-cc65/src/human.c rename to examples/defunct-explorations/cc65-Chess-2/apple2-cc65/src/human.c diff --git a/examples/cc65-Chess/apple2-cc65/src/human.h b/examples/defunct-explorations/cc65-Chess-2/apple2-cc65/src/human.h similarity index 100% rename from examples/cc65-Chess/apple2-cc65/src/human.h rename to examples/defunct-explorations/cc65-Chess-2/apple2-cc65/src/human.h diff --git a/examples/cc65-Chess/apple2-cc65/src/main.c b/examples/defunct-explorations/cc65-Chess-2/apple2-cc65/src/main.c similarity index 100% rename from examples/cc65-Chess/apple2-cc65/src/main.c rename to examples/defunct-explorations/cc65-Chess-2/apple2-cc65/src/main.c diff --git a/examples/cc65-Chess/apple2-cc65/src/plat.h b/examples/defunct-explorations/cc65-Chess-2/apple2-cc65/src/plat.h similarity index 100% rename from examples/cc65-Chess/apple2-cc65/src/plat.h rename to examples/defunct-explorations/cc65-Chess-2/apple2-cc65/src/plat.h diff --git a/examples/cc65-Chess/apple2-cc65/src/types.h b/examples/defunct-explorations/cc65-Chess-2/apple2-cc65/src/types.h similarity index 100% rename from examples/cc65-Chess/apple2-cc65/src/types.h rename to examples/defunct-explorations/cc65-Chess-2/apple2-cc65/src/types.h diff --git a/examples/defunct-explorations/chess/apple2-cc65/chess.apple2 b/examples/defunct-explorations/chess/apple2-cc65/chess.apple2 index 3d18893..6fed23c 100644 Binary files a/examples/defunct-explorations/chess/apple2-cc65/chess.apple2 and b/examples/defunct-explorations/chess/apple2-cc65/chess.apple2 differ diff --git a/examples/defunct-explorations/chess/apple2-cc65/chess.apple2enh b/examples/defunct-explorations/chess/apple2-cc65/chess.apple2enh new file mode 100644 index 0000000..259d5e4 Binary files /dev/null and b/examples/defunct-explorations/chess/apple2-cc65/chess.apple2enh differ diff --git a/examples/defunct-explorations/chess/apple2-cc65/chess.dsk b/examples/defunct-explorations/chess/apple2-cc65/chess.dsk new file mode 100644 index 0000000..de7f2ab Binary files /dev/null and b/examples/defunct-explorations/chess/apple2-cc65/chess.dsk differ diff --git a/examples/defunct-explorations/chess/apple2-cc65/obj/apple2/apple2idiot.d b/examples/defunct-explorations/chess/apple2-cc65/obj/apple2/apple2idiot.d index ad33b9a..aedde3d 100644 --- a/examples/defunct-explorations/chess/apple2-cc65/obj/apple2/apple2idiot.d +++ b/examples/defunct-explorations/chess/apple2-cc65/obj/apple2/apple2idiot.d @@ -1,4 +1,4 @@ -obj/apple2/apple2idiot.o: src/apple2idiot.c src/globals.h src/apple2idiot.h src/../../../../arduino-lib/Apple2Idiot/A2I_commands.h +obj/apple2/apple2idiot.o: src/apple2idiot.c src/globals.h src/apple2idiot.h src/A2I_commands.h -src/apple2idiot.c src/globals.h src/apple2idiot.h src/../../../../arduino-lib/Apple2Idiot/A2I_commands.h: +src/apple2idiot.c src/globals.h src/apple2idiot.h src/A2I_commands.h: diff --git a/examples/defunct-explorations/chess/apple2-cc65/obj/apple2/main.d b/examples/defunct-explorations/chess/apple2-cc65/obj/apple2/main.d index 9fd0796..fc815cb 100644 --- a/examples/defunct-explorations/chess/apple2-cc65/obj/apple2/main.d +++ b/examples/defunct-explorations/chess/apple2-cc65/obj/apple2/main.d @@ -1,4 +1,4 @@ -obj/apple2/main.o: src/main.c src/globals.h src/menu.h src/apple2idiot.h src/../../chess_commands.h src/../../../../arduino-lib/Apple2Idiot/A2I_commands.h +obj/apple2/main.o: src/main.c src/globals.h src/menu.h src/apple2idiot.h src/../../chess_commands.h src/A2I_commands.h -src/main.c src/globals.h src/menu.h src/apple2idiot.h src/../../chess_commands.h src/../../../../arduino-lib/Apple2Idiot/A2I_commands.h: +src/main.c src/globals.h src/menu.h src/apple2idiot.h src/../../chess_commands.h src/A2I_commands.h: diff --git a/examples/defunct-explorations/chess/apple2-cc65/obj/apple2enh/apple2idiot.d b/examples/defunct-explorations/chess/apple2-cc65/obj/apple2enh/apple2idiot.d new file mode 100644 index 0000000..38011b1 --- /dev/null +++ b/examples/defunct-explorations/chess/apple2-cc65/obj/apple2enh/apple2idiot.d @@ -0,0 +1,4 @@ +obj/apple2enh/apple2idiot.o: src/apple2idiot.c src/globals.h src/apple2idiot.h src/A2I_commands.h + +src/apple2idiot.c src/globals.h src/apple2idiot.h src/A2I_commands.h: + diff --git a/examples/defunct-explorations/chess/apple2-cc65/obj/apple2enh/main.d b/examples/defunct-explorations/chess/apple2-cc65/obj/apple2enh/main.d new file mode 100644 index 0000000..03573c7 --- /dev/null +++ b/examples/defunct-explorations/chess/apple2-cc65/obj/apple2enh/main.d @@ -0,0 +1,4 @@ +obj/apple2enh/main.o: src/main.c src/globals.h src/menu.h src/apple2idiot.h src/../../chess_commands.h src/A2I_commands.h + +src/main.c src/globals.h src/menu.h src/apple2idiot.h src/../../chess_commands.h src/A2I_commands.h: + diff --git a/examples/defunct-explorations/chess/apple2-cc65/obj/apple2enh/menu.d b/examples/defunct-explorations/chess/apple2-cc65/obj/apple2enh/menu.d new file mode 100644 index 0000000..77a8585 --- /dev/null +++ b/examples/defunct-explorations/chess/apple2-cc65/obj/apple2enh/menu.d @@ -0,0 +1,4 @@ +obj/apple2enh/menu.o: src/menu.c src/globals.h src/menu.h + +src/menu.c src/globals.h src/menu.h: + diff --git a/examples/defunct-explorations/chess/apple2-cc65/src/A2I_commands.h b/examples/defunct-explorations/chess/apple2-cc65/src/A2I_commands.h new file mode 100644 index 0000000..60ef6f3 --- /dev/null +++ b/examples/defunct-explorations/chess/apple2-cc65/src/A2I_commands.h @@ -0,0 +1,55 @@ +#ifndef A2I_COMMANDS_H +#define A2I_COMMANDS_H +// +// Addresses, Commands and messages that are communicated between the ESP and the Apple +// via the dual-port ram. + + +// Address offsets +//#define RAM_A2I 0 +//#define RAM_APPLE 1 +//#define SHARED_RAM_START_ADDRESS 2 + + +#define MAIN_LOOP_INTERVAL 3 // Unimplemented +#define EOT 4 // End of transmit +#define ERR 5 // Error +#define ACK 6 // Acknowledge + +#define COMMAND_NOT_FOUND 7 + +/*################################################ +# GENERIC STATUSES # +################################################*/ +#define MORE_TO_SEND 10 +#define NOT_FOUND 11 + +/*################################################ +# WIFI # +################################################*/ +#define COMMAND_WIFI_SCAN 21 +#define COMMAND_WIFI_CONNECT 22 +#define COMMAND_WIFI_DISCONNECT 23 // Unimplemented +#define WIFI_CONNECTED 24 // Unimplemented +#define WIFI_NOT_CONNECTED 25 // Unimplemented + +/*################################################ +# PROGRAM IDS # +################################################*/ + +#define APP_WEATHER 200 +#define APP_CHESS 201 +#define APP_ISS 202 + + +/*################################################ +# SLACK (50) # +################################################*/ +#define COMMAND_GET_N_CHANNELS 50 // Return number of channels +#define COMMAND_GET_CHANNEL_N 51 // Return info for channel identified by #n +#define COMMAND_GET_CHANNEL_STR 52 // Return info for channel identified by string (id or name) +#define COMMAND_SET_CHANNEL 53 +#define COMMAND_SEND_MESSAGE 54 +#define COMMAND_GET_MESSAGES 55 + +#endif diff --git a/examples/defunct-explorations/chess/apple2-cc65/src/apple2idiot.c b/examples/defunct-explorations/chess/apple2-cc65/src/apple2idiot.c index fe2a948..67e9fca 120000 --- a/examples/defunct-explorations/chess/apple2-cc65/src/apple2idiot.c +++ b/examples/defunct-explorations/chess/apple2-cc65/src/apple2idiot.c @@ -1 +1 @@ -../../../../cc65-lib/apple2idiot.c \ No newline at end of file +../../../../../cc65-lib/apple2idiot.c \ No newline at end of file diff --git a/examples/defunct-explorations/chess/apple2-cc65/src/apple2idiot.h b/examples/defunct-explorations/chess/apple2-cc65/src/apple2idiot.h index 3532461..14304f6 120000 --- a/examples/defunct-explorations/chess/apple2-cc65/src/apple2idiot.h +++ b/examples/defunct-explorations/chess/apple2-cc65/src/apple2idiot.h @@ -1 +1 @@ -../../../../cc65-lib/apple2idiot.h \ No newline at end of file +../../../../../cc65-lib/apple2idiot.h \ No newline at end of file diff --git a/examples/defunct-explorations/chess/apple2-cc65/src/main.c b/examples/defunct-explorations/chess/apple2-cc65/src/main.c index 3983cbf..e23c8f1 100644 --- a/examples/defunct-explorations/chess/apple2-cc65/src/main.c +++ b/examples/defunct-explorations/chess/apple2-cc65/src/main.c @@ -9,7 +9,7 @@ #include "menu.h" #include "apple2idiot.h" #include "../../chess_commands.h" -#include "../../../../arduino-lib/Apple2Idiot/A2I_commands.h" +#include "A2I_commands.h" // Stuff from StewBC/cc65-Chess/ //#include "StewBC/plat.h" @@ -66,6 +66,7 @@ void popup(unsigned char* message) { void main(void) { unsigned char key; + write_byte(APPLE_COMMAND_ADDRESS, 201); while (state != STATE_QUIT) { switch(state) { case STATE_LOCAL_GAME: diff --git a/examples/template/a2i_chess.cpp b/examples/old_basic_examples/template/a2i_chess.cpp similarity index 100% rename from examples/template/a2i_chess.cpp rename to examples/old_basic_examples/template/a2i_chess.cpp diff --git a/examples/old_basic_examples/template/a2i_chess.h b/examples/old_basic_examples/template/a2i_chess.h new file mode 100644 index 0000000..bb691b0 --- /dev/null +++ b/examples/old_basic_examples/template/a2i_chess.h @@ -0,0 +1,66 @@ +#ifndef A2I_CHESS_H +#define A2I_CHESS_H + +#include +#include +#include +#include +#include "chess_commands.h" + + +#define MAX_GAME_SIZE 110 * 4 // This is probably not enough, but it's fine for development. + // https://chess.stackexchange.com/questions/2506/what-is-the-average-length-of-a-game-of-chess + // times four because one move is "e7e5" + +class Chess { + + public: + byte appId = APP_CHESS; // This is "registered" with A2I_commands.h which is part of Apple2Idiot.h + // This id is sent from the Apple to the ESP to tell the esp what app + // is currently active. The main loop of the ESP sketch then knows to use + // this class to respond to incoming commands from the Apple. + + + char game_string[MAX_GAME_SIZE]; // This is probably not enough, but it's fine for development. + + char game_status[25]; + char last_player_move[5]; // "a2a4" + char last_ai_move[5]; // "8g76" + + //char game_board[9][20]; + char game_board[9][22] = { + "8 r n b q k b n r", + "7 p p p p p p p p", + "6 . . . . . . . .", + "5 . . . . . . . .", + "4 . . . . . . . .", + "3 . . . . . . . .", + "2 P P P P P P P P", + "1 R N B Q K B N R", + " a b c d e f g h" + }; + + void init(Apple2Idiot *a2ip, HTTPClient *httpp); + char* getGameStatus(char* game_status); + byte makeMove(String move_string); + byte handleCommand(byte command); + byte validateMove(String move_string); + char* getAIMove(); + void getBoard(); + + private: + + Apple2Idiot *a2i; + HTTPClient *http; + + void removeSubstr (char *string, char *sub); + const char api_entry_point[32] = "http://chess-api.herokuapp.com"; + /* Remember, flexible array won't work + * in a class, so don't try to do this... + * const char foo[] = "hello world"; + */ + +}; + +#endif + diff --git a/examples/old_basic_examples/template/chess_commands.h b/examples/old_basic_examples/template/chess_commands.h new file mode 100644 index 0000000..7721c90 --- /dev/null +++ b/examples/old_basic_examples/template/chess_commands.h @@ -0,0 +1,38 @@ +#ifndef A2I_CHESS_COMMANDS_H +#define A2I_CHESS_COMMANDS_H + +/* Apple II <-> ESP Commands */ +#define CHESS_NEW_GAME 10 +#define CHESS_GET_AI_MOVE 20 +#define CHESS_GET_GAME_STATUS 22 +#define CHESS_GET_BOARD 23 +#define CHESS_MAKE_MOVE 21 + +/* Responses */ +#define CHESS_INVALID_MOVE 123 +#define CHESS_VALID_MOVE 124 + +/* + * Responses from API... + * "in_progress" + * "black_won" + * "white_won" + * "white_won_resign" + * "black_won_resign" + * "stalemate" + * "insufficient_material" + * "fifty_rule_move" + * "threefold_repitition" + * "unknown" + */ + +#define STATUS_IN_PROGRESS 200 +#define STATUS_BLACK_WON 201 +#define STATUS_WHITE_WON 202 +#define STATUS_STALEMATE 205 +#define STATUS_FIFTY_RULE_MOVE 207 +#define STATUS_UNKNOWN 209 +#define STATUS_UNHANDLED 210 +#define STATUS_ERROR 211 + +#endif diff --git a/examples/template/template.dsk b/examples/old_basic_examples/template/template.dsk similarity index 100% rename from examples/template/template.dsk rename to examples/old_basic_examples/template/template.dsk diff --git a/examples/template/template.ino b/examples/old_basic_examples/template/template.ino similarity index 100% rename from examples/template/template.ino rename to examples/old_basic_examples/template/template.ino