commit
308931ddda
|
@ -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
|
||||
|
||||
|
||||
/*################################################
|
||||
|
|
|
@ -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.
|
@ -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
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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):"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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.
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
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.
|
@ -0,0 +1,4 @@
|
|||
obj/apple2/apple2idiot.o: src/apple2idiot.c src/apple2idiot.h
|
||||
|
||||
src/apple2idiot.c src/apple2idiot.h:
|
||||
|
|
@ -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:
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
obj/apple2/map.o: src/map.c
|
||||
|
||||
src/map.c:
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
obj/apple2/osd.o: src/osd.c
|
||||
|
||||
src/osd.c:
|
||||
|
|
@ -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:
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
obj/apple2/sp.o: src/sp.c src/sp.h
|
||||
|
||||
src/sp.c src/sp.h:
|
||||
|
Binary file not shown.
Binary file not shown.
|
@ -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
|
|
@ -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--;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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 */
|
|
@ -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 */
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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 */
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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--;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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 */
|
|
@ -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));
|
||||
}
|
|
@ -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 */
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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 */
|
|
@ -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 */
|
|
@ -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 */
|
||||
|
|
@ -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
|
|
@ -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
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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
|
||||
```
|
|
@ -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.
|
||||
}
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
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.
|
@ -0,0 +1,4 @@
|
|||
obj/apple2/apple2idiot.o: src/apple2idiot.c src/apple2idiot.h
|
||||
|
||||
src/apple2idiot.c src/apple2idiot.h:
|
||||
|
|
@ -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:
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
obj/apple2/map.o: src/map.c
|
||||
|
||||
src/map.c:
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
obj/apple2/osd.o: src/osd.c
|
||||
|
||||
src/osd.c:
|
||||
|
|
@ -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
Loading…
Reference in New Issue