Merge pull request #5 from equant/4-multi-app-esp32

4 multi app esp32
This commit is contained in:
Nathanial Hendler 2022-07-26 09:14:37 -07:00 committed by GitHub
commit 308931ddda
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
174 changed files with 6730 additions and 22 deletions

View File

@ -37,8 +37,10 @@
# PROGRAM IDS #
################################################*/
#define APP_WEATHER 200 // Unimplemented
#define APP_CHESS 201 // Unimplemented
#define APP_WEATHER 200
#define APP_CHESS 201
#define APP_ISS 202
#define APP_DND5EAPI 202
/*################################################

View File

@ -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,

Binary file not shown.

View File

@ -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

View File

@ -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 <WiFi.h>
#include <Apple2Idiot.h>
#include <ArduinoJson.h>
#include <HTTPClient.h>
Apple2Idiot a2i = Apple2Idiot();
#define COMMAND_SET_COUNTRY 20
#define COMMAND_SET_CITY 21
#define COMMAND_FETCH_WEATHER 25
#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.begin(115200);
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");
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();
}
}

View File

@ -0,0 +1,13 @@
#ifndef A2I_CHESS_COMMANDS_H
#define A2I_CHESS_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

View File

@ -23,15 +23,15 @@
1010 WS$ = CC$
1020 WA = AA +1
1030 GOSUB 10000
1040 POKE AA,200 : REM SET COUNTRY
1040 POKE AA,20 : REM SET COUNTRY
1050 GOTO 100
2000 INPUT "CITY: ";CITY$
2010 WS$ = CITY$
2020 WA = AA +1
2030 GOSUB 10000
2040 POKE AA,201 : REM SET CITY
2040 POKE AA,21 : REM SET CITY
2050 GOTO 100
3000 POKE AA,205 : REM FETCH WEATHER
3000 POKE AA,25 : REM FETCH WEATHER
3010 GOTO 100
4000 HOME
4005 MAXSTR = 0

View File

@ -1,4 +1,4 @@
diskname='15_fixed_essid_weather-cc65.dsk'
diskname='15_fixed_essid_weather-basic.dsk'
echo "-------------------------"
echo "Files on disk image (before):"

View File

@ -15,8 +15,12 @@
#define APPLE_COMMAND_ADDRESS 0xC201
#define RAM_DATA_START_ADDRESS 0xC202
#define COMMAND_SET_COUNTRY 200
#define COMMAND_SET_CITY 201
#define COMMAND_FETCH_WEATHER 205
#define APP_ID 200
#define COMMAND_SET_COUNTRY 20
#define COMMAND_SET_CITY 21
#define COMMAND_FETCH_WEATHER 25
#define DEFAULT_COUNTRY_CODE "US"
#endif

View File

@ -23,9 +23,12 @@ unsigned int address_offset = 0;
void main(void)
{
unsigned char key;
clrscr();
write_byte(APPLE_COMMAND_ADDRESS, APP_ID);
while (key != ASCII_5) { // Last menu item should be Quit
drawMenuBar();
gotoxy(0,2);

View File

@ -7,8 +7,8 @@
#include "menu.h"
#define MENU_LENGTH 5
#define MENU_WORD_LENGTH 8
unsigned char menuEntries[MENU_LENGTH][MENU_WORD_LENGTH] = {"Country", "City", "Fetch", "Display", "Quit"};
#define MENU_WORD_LENGTH 9
unsigned char menuEntries[MENU_LENGTH][MENU_WORD_LENGTH] = {"Country ", "City ", "Fetch ", "Display ", "Quit"};
void drawMenuBar() {
static unsigned char i;

View File

@ -0,0 +1,168 @@
/*
Nathanial Hendler
2021
github.com/equant
*/
// Load Wi-Fi library
#include <WiFi.h>
#include <Apple2Idiot.h>
#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();
/*################################################
# 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.
################################################*/
Chess chess_app = Chess();
Weather weather_app = Weather();
Iss iss_app = Iss();
#define N_APPS 3
byte app_ids[N_APPS] = {APP_ISS, APP_WEATHER, APP_CHESS};
/*******************/
/* 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();
//chess_app.init(&a2i, &http);
//weather_app.init(&a2i, &http);
#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<N_APPS; i++) {
Serial.print("App loop: ");
Serial.println(i);
Serial.print("Checking ID: ");
Serial.println(app_ids[i]);
if (command_byte == app_ids[i]) {
// We were sent an app id.
Serial.println("We were sent an app id");
if (current_app_id != command_byte) {
// We are talking to a new app
Serial.println("We are talking to a new app");
a2i.write_data(ESP_COMMAND_ADDRESS, ACK);
current_app_id = command_byte;
}
a2i.write_data(APPLE_COMMAND_ADDRESS, ACK);
a2i.write_data(ESP_COMMAND_ADDRESS, EOT);
lastMainLoopTime = millis();
Serial.println("About to return");
return;
}
}
/* If we got here, we need to pass the command to app's class to be handled */
if (current_app_id == weather_app.appId) {
Serial.println("Received a command for Weather()");
weather_app.handleCommand(command_byte);
Serial.println("...command for Weather() 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 if (current_app_id == iss_app.appId) {
Serial.println("Received a command for ISS()");
iss_app.handleCommand(command_byte);
Serial.println("...command for ISS() 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 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()");
}
}
lastMainLoopTime = millis();
}
}

View File

@ -0,0 +1,8 @@
# Arduno sketch for Apple2 Internet Weather Client Examples
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

View File

@ -0,0 +1,291 @@
#include <stdio.h>
#include <string.h>
#include <Arduino.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>
#include <Apple2Idiot.h>
#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<char *>();
}
} 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<char *>();
}
} 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 *>());
char* pch = NULL;
pch = strtok((char *)doc["board"].as<char *>(), "\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.
}

View File

@ -0,0 +1,92 @@
#include <stdio.h>
#include <string.h>
#include <Arduino.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>
#include <Apple2Idiot.h>
#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;
}

View File

@ -0,0 +1,43 @@
#ifndef A2I_ISS_H
#define A2I_ISS_H
#include <Arduino.h>
#include <HTTPClient.h>
#include <A2I_commands.h>
#include <Apple2Idiot.h>
#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

View File

@ -0,0 +1,119 @@
#include <stdio.h>
#include <string.h>
#include <Arduino.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>
#include <Apple2Idiot.h>
#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;
}

View File

@ -0,0 +1,47 @@
#ifndef A2I_WEATHER_H
#define A2I_WEATHER_H
#include <Arduino.h>
#include <HTTPClient.h>
#include <A2I_commands.h>
#include <Apple2Idiot.h>
#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

View File

@ -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

View File

@ -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

View File

@ -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 <dist.apple2/iss.system
java -jar dist.apple2/ac.jar -as dist.apple2/dist.po iss bin <iss
java -jar dist.apple2/ac.jar -p dist.apple2/dist.po MAP.HGR bin <gfx/MAP.HGR
cp dist.apple2/dist.po /run/media/equant/BMOW/equant/
clean:
$(call RMFILES,$(OBJECTS))
$(call RMFILES,$(DEPENDS))
$(call RMFILES,$(REMOVES))
$(call RMFILES,$(PROGRAM))
$(call RMFILES,*.map)
else # $(words $(TARGETLIST)),1
all test clean:
$(foreach t,$(TARGETLIST),$(MAKE) TARGETS=$t $@$(NEWLINE))
endif # $(words $(TARGETLIST)),1
OBJDIRLIST := $(wildcard $(OBJDIR)/*)
zap:
$(foreach o,$(OBJDIRLIST),-$(call RMFILES,$o/*.o $o/*.d $o/*.lst)$(NEWLINE))
$(foreach o,$(OBJDIRLIST),-$(call RMDIR,$o)$(NEWLINE))
-$(call RMDIR,$(OBJDIR))
-$(call RMFILES,$(basename $(PROGRAM)).* $(STATEFILE))
love:
@echo "Not war, eh?"
###################################################################
### Place your additional targets in the additional Makefiles ###
### in the same directory - their names have to end with ".mk"! ###
###################################################################
-include *.mk

View File

@ -0,0 +1,352 @@
###############################################################################
### 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 apple.ip65
# 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 <dist.apple2/iss.system
java -jar dist.apple2/ac.jar -as dist.apple2/dist.po iss bin <iss
java -jar dist.apple2/ac.jar -p dist.apple2/dist.po MAP.HGR bin <gfx/MAP.HGR
clean:
$(call RMFILES,$(OBJECTS))
$(call RMFILES,$(DEPENDS))
$(call RMFILES,$(REMOVES))
$(call RMFILES,$(PROGRAM))
$(call RMFILES,*.map)
else # $(words $(TARGETLIST)),1
all test clean:
$(foreach t,$(TARGETLIST),$(MAKE) TARGETS=$t $@$(NEWLINE))
endif # $(words $(TARGETLIST)),1
OBJDIRLIST := $(wildcard $(OBJDIR)/*)
zap:
$(foreach o,$(OBJDIRLIST),-$(call RMFILES,$o/*.o $o/*.d $o/*.lst)$(NEWLINE))
$(foreach o,$(OBJDIRLIST),-$(call RMDIR,$o)$(NEWLINE))
-$(call RMDIR,$(OBJDIR))
-$(call RMFILES,$(basename $(PROGRAM)).* $(STATEFILE))
love:
@echo "Not war, eh?"
###################################################################
### Place your additional targets in the additional Makefiles ###
### in the same directory - their names have to end with ".mk"! ###
###################################################################
-include *.mk

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

View File

@ -0,0 +1,4 @@
obj/apple2/apple2idiot.o: src/apple2idiot.c src/apple2idiot.h
src/apple2idiot.c src/apple2idiot.h:

View File

@ -0,0 +1,4 @@
obj/apple2/main.o: src/main.c src/map.h src/satellite.h src/osd.h src/apple2idiot.h
src/main.c src/map.h src/satellite.h src/osd.h src/apple2idiot.h:

View File

@ -0,0 +1,4 @@
obj/apple2/map.o: src/map.c
src/map.c:

View File

@ -0,0 +1,4 @@
obj/apple2/osd.o: src/osd.c
src/osd.c:

View File

@ -0,0 +1,4 @@
obj/apple2/satellite.o: src/satellite.c src/satellite.h src/apple2idiot.h
src/satellite.c src/satellite.h src/apple2idiot.h:

View File

@ -0,0 +1,4 @@
obj/apple2/sp.o: src/sp.c src/sp.h
src/sp.c src/sp.h:

View File

@ -0,0 +1,412 @@
#ifndef _IP65_H
#define _IP65_H
#include <stdint.h>
#include <stdbool.h>
// 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

View File

@ -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 <conio.h>
#include <tgi.h>
#include <apple2.h>
#include <stdbool.h>
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#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--;
}
}
}

View File

@ -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 */

View File

@ -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 */

View File

@ -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 <apple2.h>
#include <tgi.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <conio.h>
#include <time.h>
#include <stdio.h>
#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;
}
}
}

View File

@ -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 */

View File

@ -0,0 +1,87 @@
#include <stdio.h>
#include <string.h>
#include <conio.h>
#include <peekpoke.h>
//#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<size; i++) {
write_byte(address+i, string_to_send[i]);
}
if (string_to_send[size-1] != '\0') {
write_byte(address+i, '\0');
}
}
// HOW DO I PASS THE ADDRESS TO read_string_from_ram ???
void read_string_from_ram(unsigned int address, char *data, char length) {
unsigned char c, i;
//gotoxy(0,0);
//printf("A: %u, L: %u\n", address, length);
//cgetc();
for (i=0; i<length; i++) {
c = PEEK(address + i);
//printf("PEEK[%u]: %c\n", i, c);
data[i] = c;
if (c == 0) {
break;
}
}
}

View File

@ -0,0 +1,10 @@
#ifndef _A2I_H
#define _A2I_H
unsigned int read_byte(unsigned int address);
unsigned char write_byte(unsigned int address, unsigned char byte_to_write);
unsigned char* write_string_to_ram(unsigned int address, char* string_to_send);
void read_string_from_ram(unsigned int address, char *data, char length);
unsigned char write_byte_wait_for_ack(unsigned int address, unsigned char byte_to_write);
#endif

View File

@ -0,0 +1,70 @@
/**
* ISS Tracker
*
* @author Thomas Cherryhomes
* @email thom dot cherryhomes at gmail dot com
* @license gpl v. 3, see LICENSE.md for details.
*/
#include <conio.h>
#include <tgi.h>
#include <apple2.h>
#include <stdbool.h>
#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--;
}
}
}

View File

@ -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 <apple2.h>
#include <6502.h>
#include <peekpoke.h>
#include <conio.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
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);
}

View File

@ -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 */

View File

@ -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 <conio.h>
#include <apple2.h>
#include <time.h>
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));
}

View File

@ -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 */

View File

@ -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 <apple2.h>
#include <tgi.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <conio.h>
#include <unistd.h>
#include <time.h>
#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;
}
}
}

View File

@ -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 */

View File

@ -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 <conio.h>
#include <stdio.h>
#include <apple2.h>
#include <peekpoke.h>
#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 */

View File

@ -0,0 +1,30 @@
/**
* FujiNet CONFIG for #Apple2
*
* SmartPort MLI Routines
*/
#ifndef SP_H
#define SP_H
#include <stdint.h>
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 */

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,154 @@
/*
Nathanial Hendler
2021
github.com/equant
*/
// Load Wi-Fi library
#include <WiFi.h>
#include <Apple2Idiot.h>
#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<N_APPS; i++) {
Serial.print("App loop: ");
Serial.println(i);
Serial.print("Checking ID: ");
Serial.println(app_ids[i]);
if (command_byte == app_ids[i]) {
// We were sent an app id.
Serial.println("We were sent an app id");
if (current_app_id != command_byte) {
// We are talking to a new app
Serial.println("We are talking to a new app");
a2i.write_data(ESP_COMMAND_ADDRESS, ACK);
current_app_id = command_byte;
}
a2i.write_data(APPLE_COMMAND_ADDRESS, ACK);
a2i.write_data(ESP_COMMAND_ADDRESS, EOT);
lastMainLoopTime = millis();
Serial.println("About to return");
return;
}
}
/* If we got here, we need to pass the command to app's class to be handled */
if (current_app_id == dnd5eapi_app.appId) {
Serial.println("Received a command for Dnd5eapi()");
dnd5eapi_app.handleCommand(command_byte);
Serial.println("...command for Dnd5eapi() 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()");
}
}
lastMainLoopTime = millis();
}
}

View File

@ -0,0 +1,3 @@
```bash
if arduino-cli compile --fqbn esp32:esp32:nodemcu-32s .; then arduino-cli upload -p /dev/ttyUSB0 --fqbn esp32:esp32:esp32-poe-iso .; fi
```

View File

@ -0,0 +1,291 @@
#include <stdio.h>
#include <string.h>
#include <Arduino.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>
#include <Apple2Idiot.h>
#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<char *>();
}
} 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<char *>();
}
} 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 *>());
char* pch = NULL;
pch = strtok((char *)doc["board"].as<char *>(), "\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.
}

View File

@ -0,0 +1,66 @@
#ifndef A2I_CHESS_H
#define A2I_CHESS_H
#include <Arduino.h>
#include <HTTPClient.h>
#include <A2I_commands.h>
#include <Apple2Idiot.h>
#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

View File

@ -0,0 +1,100 @@
#include <stdio.h>
#include <string.h>
#include <Arduino.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>
#include <Apple2Idiot.h>
#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/monsters?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("----------------------");
int result_count = doc["count"];
String results = doc["results"];
/// Write to ram
int address_counter = SHARED_RAM_START_ADDRESS + 1;
if (result_count == 0) {
Serial.println("no monsters found");
a2i.write_data(0, SHARED_RAM_START_ADDRESS);
} else {
a2i.write_data(result_count, address_counter);
for (int i = 0; i < result_count; ++i) {
Serial.print(i + 1);
Serial.print(": ");
Serial.println(doc["results"][i]["index"].as<String>());
//delay(10);
address_counter = a2i.write_string_to_shared_ram(doc["results"][i]["index"].as<String>(), address_counter + 1);
}
}
}
result = ACK;
}
else {
Serial.println("Error on HTTP request");
result = ERR;
}
http.end(); //Free the resources
return result;
}

View File

@ -0,0 +1,42 @@
#ifndef A2I_DND5EAPI_H
#define A2I_DND5EAPI_H
#include <Arduino.h>
#include <HTTPClient.h>
#include <A2I_commands.h>
#include <Apple2Idiot.h>
#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 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

View File

@ -0,0 +1,92 @@
#include <stdio.h>
#include <string.h>
#include <Arduino.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>
#include <Apple2Idiot.h>
#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;
}

View File

@ -0,0 +1,43 @@
#ifndef A2I_ISS_H
#define A2I_ISS_H
#include <Arduino.h>
#include <HTTPClient.h>
#include <A2I_commands.h>
#include <Apple2Idiot.h>
#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

View File

@ -0,0 +1,119 @@
#include <stdio.h>
#include <string.h>
#include <Arduino.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>
#include <Apple2Idiot.h>
#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;
}

View File

@ -0,0 +1,47 @@
#ifndef A2I_WEATHER_H
#define A2I_WEATHER_H
#include <Arduino.h>
#include <HTTPClient.h>
#include <A2I_commands.h>
#include <Apple2Idiot.h>
#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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 <dist.apple2/iss.system
java -jar dist.apple2/ac.jar -as dist.apple2/dist.po iss bin <iss
java -jar dist.apple2/ac.jar -p dist.apple2/dist.po MAP.HGR bin <gfx/MAP.HGR
cp dist.apple2/dist.po /run/media/equant/BMOW/equant/
clean:
$(call RMFILES,$(OBJECTS))
$(call RMFILES,$(DEPENDS))
$(call RMFILES,$(REMOVES))
$(call RMFILES,$(PROGRAM))
$(call RMFILES,*.map)
else # $(words $(TARGETLIST)),1
all test clean:
$(foreach t,$(TARGETLIST),$(MAKE) TARGETS=$t $@$(NEWLINE))
endif # $(words $(TARGETLIST)),1
OBJDIRLIST := $(wildcard $(OBJDIR)/*)
zap:
$(foreach o,$(OBJDIRLIST),-$(call RMFILES,$o/*.o $o/*.d $o/*.lst)$(NEWLINE))
$(foreach o,$(OBJDIRLIST),-$(call RMDIR,$o)$(NEWLINE))
-$(call RMDIR,$(OBJDIR))
-$(call RMFILES,$(basename $(PROGRAM)).* $(STATEFILE))
love:
@echo "Not war, eh?"
###################################################################
### Place your additional targets in the additional Makefiles ###
### in the same directory - their names have to end with ".mk"! ###
###################################################################
-include *.mk

View File

@ -0,0 +1,352 @@
###############################################################################
### 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 apple.ip65
# 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 <dist.apple2/iss.system
java -jar dist.apple2/ac.jar -as dist.apple2/dist.po iss bin <iss
java -jar dist.apple2/ac.jar -p dist.apple2/dist.po MAP.HGR bin <gfx/MAP.HGR
clean:
$(call RMFILES,$(OBJECTS))
$(call RMFILES,$(DEPENDS))
$(call RMFILES,$(REMOVES))
$(call RMFILES,$(PROGRAM))
$(call RMFILES,*.map)
else # $(words $(TARGETLIST)),1
all test clean:
$(foreach t,$(TARGETLIST),$(MAKE) TARGETS=$t $@$(NEWLINE))
endif # $(words $(TARGETLIST)),1
OBJDIRLIST := $(wildcard $(OBJDIR)/*)
zap:
$(foreach o,$(OBJDIRLIST),-$(call RMFILES,$o/*.o $o/*.d $o/*.lst)$(NEWLINE))
$(foreach o,$(OBJDIRLIST),-$(call RMDIR,$o)$(NEWLINE))
-$(call RMDIR,$(OBJDIR))
-$(call RMFILES,$(basename $(PROGRAM)).* $(STATEFILE))
love:
@echo "Not war, eh?"
###################################################################
### Place your additional targets in the additional Makefiles ###
### in the same directory - their names have to end with ".mk"! ###
###################################################################
-include *.mk

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

View File

@ -0,0 +1,4 @@
obj/apple2/apple2idiot.o: src/apple2idiot.c src/apple2idiot.h
src/apple2idiot.c src/apple2idiot.h:

View File

@ -0,0 +1,4 @@
obj/apple2/main.o: src/main.c src/map.h src/satellite.h src/osd.h src/apple2idiot.h
src/main.c src/map.h src/satellite.h src/osd.h src/apple2idiot.h:

View File

@ -0,0 +1,4 @@
obj/apple2/map.o: src/map.c
src/map.c:

View File

@ -0,0 +1,4 @@
obj/apple2/osd.o: src/osd.c
src/osd.c:

View File

@ -0,0 +1,4 @@
obj/apple2/satellite.o: src/satellite.c src/satellite.h src/apple2idiot.h
src/satellite.c src/satellite.h src/apple2idiot.h:

Some files were not shown because too many files have changed in this diff Show More