************************************************************************************************** * CoCo WX 2.1 - Written By Todd Wallace * * So I am a bit of a weather geek. I even have my own outdoor wireless sensor that can measure * wind speed, direction, rainfall, etc. Weather "apps" are available on almost every platform * capable of connecting to the internet, so how cool would it be to do it on a CoCo too? Well I * found this cool web-based poweruser-oriented online weather service called wttr.in. It's * free to use and has a very simple implementation. I figured out how to do a simple HTTP * request over a TCP connection made with DriveWire's virtual serial port drivers and voila! * * New In Versin 2.1 * - better handling of HTTP server error responses * - better detection of locations that arent found in the search * - proper support for spaces in the location name * * New in Version 2.0 * * I have completely rewritten the networking parts of the code to request and parse weather data * in JSON format from wttr.in as that medium contains a much wider range of data and units of * measure. This now lets the user view their weather data in either metric or imperial measurements * regardless of the region they are checking the conditions of. * * The biggest change you'll actually notice is the new default full-color graphical output mode * with icons and everything! ************************************************************************************************** ; Definitions/equates STDOUT EQU 1 STDIN EQU 0 H6309 set 1 cr_lf EQU $0D0A network_signal EQU 32 keyboard_signal EQU 33 connect_timeout_signal EQU $88 wx_refresh_signal EQU $80 ;graphics_display EQU 1 ;wttr_down EQU 1 include os9.d include rbf.d include scf.d pragma cescapes ; Module header setup info MOD MODULE_SIZE,moduleName,$11,$80,START_EXEC,data_size START_MODULE ************************************************************************************** ; ----------------------------------------------------- ; Variables org 0 uRegImage RMB 2 networkPath RMB 1 gfxWindowPath RMB 1 digitGfxPath RMB 1 nilPath RMB 1 iconFilePath RMB 1 gfxStatusPath RMB 1 networkDataReady RMB 1 pendingConnect RMB 1 abortFlag RMB 1 networkTimeoutFlag RMB 1 keyInputFlag RMB 1 contentLengthFlag RMB 1 copyContentFlag RMB 1 groupID RMB 1 outputMode RMB 1 fColorSequence RMB 3 windIconIndex RMB 1 metricOutputFlag RMB 1 refreshFlag RMB 1 wxRefreshFlag RMB 1 networkBufferEndPtr RMB 2 replyBufferCurPtr RMB 2 replyBufferEndPtr RMB 2 contentBodyPtr RMB 2 jsonCurCondPtr RMB 2 shellParamPtr RMB 2 searchEndPtr RMB 2 dwReadBytes RMB 2 tempPtr RMB 2 tempChar RMB 1 tempByte RMB 1 tempWord RMB 2 tempCounter RMB 1 charDegreesSymbol RMB 1 pixelCharWidth RMB 1 jsonTableCounter RMB 1 strHexWord RMB 10 u8Value RMB 1 debugFlag RMB 1 contentLength RMB 2 constContentLength RMB 2 hpaValue RMB 2 u32Value RMB 4 ; inHg pressure conversion variables v24h RMB 1 v24m RMB 1 v24l RMB 1 pressureInches RMB 6 pdBuffer RMB 32 networkBuffer RMB 256 networkBufferSz EQU .-networkBuffer outputBuffer RMB 512 replyBuffer RMB 4096 ; json variables jsonFeelsLikeC RMB 5 jsonFeelsLikeF RMB 5 jsonHumidity RMB 4 jsonLocalTime RMB 21 jsonObsTime RMB 10 jsonPrecipInches RMB 5 jsonPrecipMM RMB 5 jsonPressure RMB 6 jsonTempC RMB 5 jsonTempF RMB 5 jsonWeatherCode RMB 5 jsonWindDir RMB 4 jsonWindSpeedKPH RMB 4 jsonWindSpeedMPH RMB 4 jsonWeatherDesc RMB 32 jsonAreaName RMB 20 jsonRegion RMB 20 stringBuffer RMB 64 gfxWindDir RMB 3 ; End of Variables ; ----------------------------------------------------- ; YOU MUST RESERVE SOME SPACE FOR STACK HERE stackSpace RMB 128 data_size EQU . ; ----------------------------------------------------- ; Constants moduleName FCS "cocowx" networkPathName FCS "/N" winPathName FCC "/W\r" nilPathName FCC "/NIL\r" gfxIconsFilename FCC "/dd/sys/cocowx/wx_icons.bin\r" gfxDigitsFilename FCC "/dd/sys/cocowx/wx_digits.bin\r" gfxCompassFilename FCC "/dd/sys/cocowx/wx_compass.bin\r" dwSelectSequence FCB $1B,$21 dwSetWindow FCB $1B,$20,8,0,0,40,25,2,0,0 FCB $1B,$35,0 ; disable scaling of coords ; setup palette colors FCB $1B,$31,0,0 FCB $1B,$31,1,53 ; orange for sun edges FCB $1B,$31,2,11 ; darker blue color for rain and dark clouds FCB $1B,$31,3,25 ; light blue for regular clouds FCB $1B,$31,4,7 ; dark gray for cloud edges FCB $1B,$31,5,54 ; yellow sun color FCB $1B,$31,6,56 ; light gray for light clouds FCB $1B,$31,7,63 ; white text FCB $1B,$31,8,27 ; cyan blue for snow flakes and sleet FCB $1B,$31,9,36 ; red for wind compass indicator FCB $1B,$31,10,16 ; green for text FCB $1B,$31,11,29 FCB $1B,$31,12,31 ; turn off the text cursor FCB $05,$20 dwSetWindowSz EQU *-dwSetWindow ; (make sure to use NULL at end) selectFontTitle FCB $1B,$3A,$C8,$42,0 selectFontInfo FCB $1B,$3A,$C8,$02,0 ; $02 = standard narrow font, $32 = Mac narrow font (no degrees) selectFontAuthor FCB $1B,$3A,$C8,$09,0 infoColorSequence FCB $1B,$32,11,0 ; 0 at end is null terminator labelColorSequence FCB $1B,$32,7,0 ; 0 at end is null terminator screenUpdateSeq FDB $1B40,0,40 ; move draw pointer FCB $1B,$32,0 ; select black color FDB $1B4A,319,199 ; draw a solid bar rectangle to blank out most of the screen screenUpdateSeqSz EQU *-screenUpdateSeq strTitle FCN "CoCo WX v2.1 - Written by Todd Wallace\r\n\n" strUsage FCC "Check live weather conditions for anywhere in the world using your CoCo! This\r\n" FCC "tool works by leveraging DriveWire (required) and the online weather info and\r\n" FCC "forecasting service wttr.in to request and retrieve live data.\r\n\n" FCC "Usage: cocowx [-t [-d hex_value]] [-m] <location>\r\n\n" FCC " - New in version 2.0 is a fully graphical output mode, complete with full-color" FCC " icons! This mode is now the default, however the traditional text-only output" FCC " can still be used by including the -t flag.\r\n" FCC " - When using the text-only mode, you can also optionally add the -d flag to\r\n" FCC " customize the degrees symbol character displayed. This is to accommodate\r\n" FCC " other OS-9 fonts with a different character set. The value must be expressed\r\n" FCC " as a 2-character hexadecimal ASCII code. (Example: cocowx -t -d F8)\r\n" FCC " - To display values in Metric units instead of Imperial (which is the default)\r\n" FCC " use the optional -m flag.\r\n" FCC " - The location parameter can be in a wide range of formats like city and state," FCC " city by itself, zipcode, etc. See http://wttr.in/help for more details.\r\n" FCC " - In graphics mode, the weather info will be auto-updated every 10 minutes,\r\n" FCC " but you can request a manual refresh at any time by pressing the ENTER key.\r\n" strUsageSz EQU *-strUsage strConnecting FCN "Connecting... " strConnectSuccess FCN "Success\r\nRequesting weather data... " strDone FCN "Done\r\n" strMsgLoadingGfx FCN "Loading graphics from disk... " strGfxRetrieving FCN " Updating WX Data: Requesting..." strGfxTimeout FCN " Error: Network Timeout" strGfxConnecting FCN " Updating WX Data: Connecting..." strConnect FCC "TCP CONNECT wttr.in 80\r" strConnectSz EQU *-strConnect strUserAborted FCN "\x03\rUser aborted.\r\n" strGetPrefix FCN "GET /" strGetSuffix FCN " HTTP/1.1\r\n" strHostInfo FCN "Host: wttr.in\r\n" strUserAgent FCN "User-Agent: curl/7.68.0\r\n\r\n" ;strWeatherFormatTxt FCN "?format=TANDY%l\\n%C\\n%x\\n%t\\n%f\\n%h\\n%P\\n%w\\n%p\\n%m\\n%S\\n%s\\n" strWeatherFormatTxt FCN "?format=j2" ;strWeatherFormatTxt FCN "?T2n" ;strGetConnection FCN "Connection: keep-alive\r\n" strConnectFailed FCN "\x03\rCould not connect to server (" strKEYWORDok FCN "OK " strKEYWORDfail FCN "FAIL " strKEYWORDcontent FCN "CONTENT-LENGTH: " strKEYWORDtong FCN "TONG" strKEYWORDhttp1.1 FCN "HTTP/1.1" strKEYWORDdoubleCRLF FCB $0D,$0A,$0D,$0A,0 ; json keywords and variable pointers jsonKeywordCurCond FCN "current_condition" strJSONfeelsLikeC FCN "FeelsLikeC" strJSONfeelsLikeF FCN "FeelsLikeF" strJSONhumidity FCN "humidity" strJSONlocalTime FCN "localObsDateTime" strJSONobsTime FCN "observation_time" strJSONprecpInches FCN "precipInches" strJSONprecipMM FCN "precipMM" strJSONpressure FCN "pressure" ; in hPa strJSONtemp_C FCN "temp_C" strJSONtemp_F FCN "temp_F" strJSONweatherCode FCN "weatherCode" strJSONwindDir FCN "winddir16Point" strJSONwindSpeedKPH FCN "windspeedKmph" strJSONwindSpeedMPH FCN "windspeedMiles" ; these keywords are for use with sub-parameters strJSONweatherDesc FCN "weatherDesc" strJSONvalue FCN "value" strJSONnearestArea FCN "nearest_area" strJSONareaName FCN "areaName" strJSONregion FCN "region" jsonKeywordVarsTable FDB strJSONfeelsLikeC ; source keyword to find FDB jsonFeelsLikeC ; destination for string to be copied to FDB strJSONfeelsLikeF FDB jsonFeelsLikeF FDB strJSONhumidity FDB jsonHumidity FDB strJSONlocalTime FDB jsonLocalTime FDB strJSONobsTime FDB jsonObsTime FDB strJSONprecpInches FDB jsonPrecipInches FDB strJSONprecipMM FDB jsonPrecipMM FDB strJSONpressure FDB jsonPressure FDB strJSONtemp_C FDB jsonTempC FDB strJSONtemp_F FDB jsonTempF FDB strJSONweatherCode FDB jsonWeatherCode FDB strJSONwindDir FDB jsonWindDir FDB strJSONwindSpeedKPH FDB jsonWindSpeedKPH FDB strJSONwindSpeedMPH FDB jsonWindSpeedMPH json_keyword_vars_sz EQU (*-jsonKeywordVarsTable)/4 ; 2 bytes per ptr, 2 ptrs per entry = 4 IFDEF wttr_down ; DEBUG REASONS XjsonTempF FCN "68" XjsonFeelsLikeF FCN "65" XjsonHumidity FCN "51" XjsonLocalTime FCN "2022-04-24 01:15 PM" XjsonPrecipInches FCN "0.0" XjsonPressure FCN "1028" XjsonWeatherCode FCN "116" XjsonWindDir FCN "NE" XjsonWindSpeedMPH FCN "19" XjsonWeatherDesc FCN "Partly cloudy" XjsonAreaName FCN "Providence" XjsonRegion FCN "Rhode Island" ENDC strBlank FCN "" testBufferSeq FCB $1B,$2D,3,2 FDB 0,0 bin32dec1M FQB 1000000 ; 1 million decimal bin32dec100K FQB 100000 ; 100 thousand decimal bin32dec10K FQB 10000 strCurrentWeather FCN "Current weather for " strLocation FCN "WEST WARWICK, RI" strTemperature FCN "\r\n\nTemperature: " strFeelsLike FCN ", Feels Like: " strHumidity FCN ", Humidity: " strPressure FCN ", Pressure: " strWind FCN "\r\nWind: " strPrecipitation FCN ", Rainfall: " strMoonPhase FCN ", Moon Phase: " strSunrise FCN "\r\nSunrise: " strSunset FCN ", Sunset: " strTimeLocal FCN " (Times All Local)" strDateTime FCN ", Observation Time: " strWindMPH FCN " mph" strWindKPH FCN " kph" strPressureInHg FCN " inHg" strPressurehPa FCN " hPa" strForecast FCN "\r\n\n3-Day Forecast: " strForecastHighLow FCN " " strPartlyCloudy FCN "Partly Cloudy" strLightSnowShowers FCN "Light Snow Showers" strGfxCoCoWX FCN "CoCo WX v2.1" strGfxAuthor FCN "Written by Todd Wallace" strGfxFeelsLike FCN "Feels Like: " strGfxHumidity FCN "Humidity: " strGfxPressure FCN "Pressure: " strGfxWind FCN "Wind" strGfxObsTime FCN "Observation Time: " strGfxRainfall FCN "Rainfall: " ; conditions weather code table (has to be 4 bytes per entry) curCondCodeTable EQU * symCloudy FCC "119" ; Cloudy FCB 0 symFog1 FCC "143" ; Fog FCB 1 symFog2 FCC "248" ; Fog (duplicate with different number) FCB 1 symFog3 FCC "260" ; Fog (duplicate with different number) FCB 1 symHeavyRain1 FCC "302" ; HeavyRain FCB 2 symHeavyRain2 FCC "308" ; HeavyRain FCB 2 symHeavyRain3 FCC "359" ; HeavyRain FCB 2 symHeavyShowers1 FCC "299" ; HeavyShowers FCB 6 symHeavyShowers2 FCC "305" ; HeavyShowers FCB 6 symHeavyShowers3 FCC "356" ; HeavyShowers FCB 6 symHeavySnow1 FCC "230" ; HeavySnow FCB 4 symHeavySnow2 FCC "329" ; HeavySnow FCB 4 symHeavySnow3 FCC "332" ; HeavySnow FCB 4 symHeavySnow4 FCC "338" ; HeavySnow FCB 4 symHeavySnowShowers1 FCC "335" ; HeavySnowShowers FCB 5 symHeavySnowShowers2 FCC "371" ; HeavySnowShowers FCB 5 symHeavySnowShowers3 FCC "395" ; HeavySnowShowers FCB 5 symLightRain1 FCC "266" ; LightRain1 FCB 3 symLightRain2 FCC "293" ; LightRain2 FCB 3 symLightRain3 FCC "296" ; LightRain3 FCB 3 symLightShowers1 FCC "176" ; LightShowers1 FCB 6 symLightShowers2 FCC "263" ; LightShowers2 FCB 6 symLightShowers3 FCC "353" ; LightShowers3 FCB 6 symLightSleet1 FCC "182" ; LightSleet1 FCB 8 symLightSleet2 FCC "185" ; LightSleet2 FCB 8 symLightSleet3 FCC "281" ; LightSleet3 FCB 8 symLightSleet4 FCC "284" ; LightSleet4 FCB 8 symLightSleet5 FCC "311" ; LightSleet5 FCB 8 symLightSleet6 FCC "314" ; LightSleet6 FCB 8 symLightSleet7 FCC "317" ; LightSleet7 FCB 8 symLightSleet8 FCC "350" ; LightSleet8 FCB 8 symLightSleet9 FCC "377" ; LightSleet9 FCB 8 symLightSleetShwrs1 FCC "179" ; LightSleetShowers FCB 8 symLightSleetShwrs2 FCC "362" ; LightSleetShowers FCB 8 symLightSleetShwrs3 FCC "365" ; LightSleetShowers FCB 8 symLightSleetShwrs4 FCC "374" ; LightSleetShowers FCB 8 symLightSnow1 FCC "227" ; LightSnow FCB 7 symLightSnow2 FCC "320" ; LightSnow FCB 7 symLightSnowShowers1 FCC "323" ; LightSnowShowers FCB 5 symLightSnowShowers2 FCC "326" ; LightSnowShowers FCB 5 symLightSnowShowers3 FCC "368" ; LightSnowShowers FCB 5 symPartlyCloudy FCC "116" ; PartlyCloudy FCB 9 symSunny FCC "113" ; Sunny FCB 10 symThunderHeavyRain FCC "389" ; ThunderyHeavyRain FCB 13 symThunderShowers1 FCC "200" ; ThunderyShowers1 FCB 11 symThunderShowers2 FCC "386" ; ThunderyShowers2 FCB 11 symThunderSnowShwrs FCC "392" ; ThunderySnowShowers FCB 5 symVeryCloudy FCC "122" ; VeryCloudy FCB 12 curCondCodeTableEnd EQU * moonphaseTable FDB strMoonNew FDB strMoonWaxCrescent FDB strMoonFirstQuarter FDB strMoonWaxGibbous FDB strMoonFull FDB strMoonWanGibbous FDB strMoonLastQuarter FDB strMoonWanCrescent ; moonphase string descriptions strMoonNew FCN "New Moon" strMoonWaxCrescent FCN "Waxing Crescent" strMoonFirstQuarter FCN "First Quarter" strMoonWaxGibbous FCN "Waxing Gibbous" strMoonFull FCN "Full Moon" strMoonWanGibbous FCN "Waning Gibbous" strMoonLastQuarter FCN "Last Quarter" strMoonWanCrescent FCN "Waning Crescent" strErrorInvalidParam FCN "Invalid parameters or syntax. Type COCOWX by itself to see usage information.\r\n" strErrorInvalidFlag FCN "Invalid flag. Type COCOWX by itself to see usage information.\r\n" strErrorNoDrivewire FCN "Error opening path to DriveWire Virtual Serial Port module /N.\r\nIs DriveWire installed correctly?\r\n" strErrorVRNmodule FCN "Error opening path to /NIL module which is needed for connection timeout timer\r\nto function. Otherwise you will have to manually exit by pressing ESC.\r\n" strErrorTimeout FCN "\x03\rError communicating with the wttr.in weather service. Connection either timed\r\nout or didn't responded to the request as expected. Is your DriveWire server\r\nconnected to the internet? Try visiting https://wttr.in from it to make sure the service is working and not having an outage.\r\n" strErrorGraphics FCN "Graphics files not found. Exiting...\r\n" strErrorLocation FCN "Location not found (" strErrorServerReply FCN "Unexpected server reply (" asciiHexList FCC "0123456789ABCDEF" ; ----------------------------------------------------- START_EXEC ************************************************************************************** * Program code area * RULE #1 - USE U TO REFERENCE ANY CHANGEABLE VARIABLES IN THE DATA AREA. * RULE #2 - USE PCR TO REFERENCE CONSTANTS SINCE THEY RESIDE WITH EXECUTABLE CODE. * RULE #3 - NEVER USE JSR FOR CALLING SUBROUTINES. ALWAYS USE BSR OR LBSR INSTEAD. ************************************************************************************** stu <uRegImage ; save copy of data area pointer in U stx <shellParamPtr ldd #$1B32 std fColorSequence,U ; lbra DEBUG_BYPASS ; init path variables lda #$FF sta <gfxWindowPath sta <iconFilePath sta <networkPath sta <nilPath sta <digitGfxPath clra sta <debugFlag sta <metricOutputFlag ; imperial units is the default so set metric to 0 sta <keyInputFlag sta <refreshFlag sta <wxRefreshFlag ; display title/author info leax strTitle,PCR lda #STDOUT lbsr PRINT_NULL_STRING ; set default char for degree symbol lda #$BE ; value for GIME text-mode screen types sta <charDegreesSymbol clr <outputMode ; set default output mode to graphics (any non-zero means text-only) ldx <shellParamPtr lbsr FIND_NEXT_NONSPACE_CHAR cmpa #C$CR lbeq DISPLAY_INFO_USAGE FLAGS_SEARCH_NEXT ; check if we have a flag lda ,X+ cmpa #C$CR lbeq ERROR_INVALID_PARAMS ; missing city/state/location which is required cmpa #'-' bne NO_MORE_FLAGS lda ,X+ ; check for degree symbol character flag lbsr CONVERT_UPPERCASE cmpa #'D' bne FLAGS_CHECK_TEXT_ONLY lbsr FIND_NEXT_NONSPACE_CHAR cmpa #C$CR lbeq ERROR_INVALID_PARAMS lbsr CONVERT_HEX_STRING_TO_BYTE ; convert the 2 character hex into a binary byte value sta <charDegreesSymbol lbsr FIND_NEXT_NONSPACE_CHAR bra FLAGS_SEARCH_NEXT FLAGS_CHECK_TEXT_ONLY cmpa #'T' bne FLAGS_CHECK_METRIC lda ,X+ cmpa #C$SPAC lbne ERROR_INVALID_PARAMS sta <outputMode ; any value non-zero will force text-only output lbsr FIND_NEXT_NONSPACE_CHAR bra FLAGS_SEARCH_NEXT FLAGS_CHECK_METRIC cmpa #'M' lbne ERROR_UNKNOWN_FLAG lda ,X+ cmpa #C$SPAC lbne ERROR_INVALID_PARAMS sta <metricOutputFlag ; any value non-zero will select metric units output lbsr FIND_NEXT_NONSPACE_CHAR bra FLAGS_SEARCH_NEXT NO_MORE_FLAGS leax -1,X ; undo auto-increment stx <shellParamPtr ; FOR TESTING WHEN WTTR.IN IS DOWN IFDEF wttr_down leay jsonTempF,U leax XjsonTempF,PCR lbsr STRING_COPY_RAW leay jsonFeelsLikeF,U leax XjsonFeelsLikeF,PCR lbsr STRING_COPY_RAW leay jsonHumidity,U leax XjsonHumidity,PCR lbsr STRING_COPY_RAW leay jsonPressure,U leax XjsonPressure,PCR lbsr STRING_COPY_RAW leay jsonPrecipInches,U leax XjsonPrecipInches,PCR lbsr STRING_COPY_RAW leay jsonWindDir,U leax XjsonWindDir,PCR lbsr STRING_COPY_RAW leay jsonWindSpeedMPH,U leax XjsonWindSpeedMPH,PCR lbsr STRING_COPY_RAW leay jsonLocalTime,U leax XjsonLocalTime,PCR lbsr STRING_COPY_RAW leay jsonWeatherDesc,U leax XjsonWeatherDesc,PCR lbsr STRING_COPY_RAW leay jsonWeatherCode,U leax XjsonWeatherCode,PCR lbsr STRING_COPY_RAW leay jsonAreaName,U leax XjsonAreaName,PCR lbsr STRING_COPY_RAW leay jsonRegion,U leax XjsonRegion,PCR lbsr STRING_COPY_RAW lbra DEBUG_WTTR_DOWN ENDC lda <nilPath bpl VRN_PATH_ALREADY_OPEN ; setup path to VRN driver for timer functionality for connection timeout lda #UPDAT. clrb leax nilPathName,PCR os9 I$Open bcc GOT_VRN_PATH ; tell user vrn is needed for the timeout timer to work leax strErrorVRNmodule,PCR lda #STDOUT lbsr PRINT_NULL_STRING lda #$FF ; this makes sure bit 7 is set so we know there is no VRN GOT_VRN_PATH sta <nilPath VRN_PATH_ALREADY_OPEN SETUP_REFRESH_WEATHER_REQUEST ldd #0 sta <networkDataReady sta <pendingConnect sta <abortFlag sta <networkTimeoutFlag leax replyBuffer,U stx <replyBufferEndPtr lda <refreshFlag bne SKIP_SIGNAL_HANDLER_SETUP ; setup the intercept stuff if not already configured leax SIGNAL_HANDLER,PCR os9 F$Icpt SKIP_SIGNAL_HANDLER_SETUP lbsr DRIVEWIRE_SETUP lbcs ERROR_NO_DRIVEWIRE ; send the connect command to drivewire to connect to wttr.in web server lda <networkPath leax strConnect,PCR ldy #strConnectSz os9 I$Write inc <pendingConnect ; setup timeout timer where if it cant connect or HTTP GET request timesout, we can gracefully fail. lda <nilPath ldb #SS.FSet ; code $C7 ldx #900 ; 15 seconds ldy #0 ldu #connect_timeout_signal os9 I$SetStt ldu <uRegImage ; tell the user we are trying to connect and sending the request to server if successful lda <refreshFlag bne MAINLOOP ; skip STDOUT message since we are in graphics mode lda #STDOUT leax strConnecting,PCR lbsr PRINT_NULL_STRING MAINLOOP lda <networkDataReady bne MAINLOOP_NEXT_READ lda <keyInputFlag bne MAINLOOP_NEW_KEYSTROKES lda <wxRefreshFlag lbne REFRESH_WEATHER_DATA ; the refresh timer elapsed and its time to refresh wx data/display it lda <abortFlag lbne USER_ABORT_EXIT lda <networkTimeoutFlag lbne ERROR_TIMEOUT_EXIT ; if here, nothing to do except wait for more incoming signals. reset network signal, and then sleep clr <networkDataReady ; reset signal for network activity lda <networkPath ldb #SS.SSig ldx #network_signal os9 I$SetStt ; sleep ldx #0 os9 F$Sleep bra MAINLOOP MAINLOOP_NEW_KEYSTROKES ; if here, there was keyboard input. handle it lda <gfxWindowPath ldb #SS.Ready os9 I$GetStt bcs MAINLOOP ; something weird happened so ignore and go through mainloop again clra tfr D,Y lda <gfxWindowPath leax stringBuffer,U os9 I$Read bcs MAINLOOP ; something weird happened so ignore and go through mainloop again clr <keyInputFlag ; reset keyInputFlag since all pending keystrokes have now been read in and cleared tfr Y,D MAINLOOP_CHECK_NEXT_KEYSTROKE lda ,X+ cmpa #C$CR lbeq REFRESH_WEATHER_DATA_MANUALLY decb bne MAINLOOP_CHECK_NEXT_KEYSTROKE ; if here, no ENTER key was pressed for a manual refresh of weather data. reset signal and go back to mainloop lda <gfxWindowPath ldb #SS.SSig ldx #keyboard_signal os9 I$SetStt bra MAINLOOP MAINLOOP_NEXT_READ lbsr DRIVEWIRE_GET_DATA bcs MAINLOOP ; check and reset signals and then sleep until more data comes through lda <pendingConnect lbeq MAINLOOP_NEW_DATA ; we are already connected so read in data from server reply ; set our string matching search routine to stop looking within temp dw network buffer ldx <networkBufferEndPtr stx <searchEndPtr leax networkBuffer,U leay strKEYWORDok,PCR lbsr STRING_SEARCH_BUFFER bcc MAINLOOP_NOW_CONNECTED leay strKEYWORDfail,PCR lbsr STRING_SEARCH_BUFFER bcs MAINLOOP_NEXT_READ ; ignore and look for further network data ; if here, connection to drivewire server failed. report reason lbsr FIND_NEXT_SPACE_NULL_CR leax 1,X lbsr FIND_NEXT_SPACE_NULL_CR leax 1,X stx <tempPtr ; disable timeout timer if exists lda <nilPath bmi MAINLOOP_SKIP_VRN_TIMER ldb #SS.FClr ldx #0 ldy #0 os9 I$SetStt MAINLOOP_SKIP_VRN_TIMER leay outputBuffer,U leax strConnectFailed,PCR lbsr STRING_COPY_RAW ldx <tempPtr lbsr STRING_COPY_CR lda #')' sta ,Y+ ldd #cr_lf std ,Y++ clr ,Y leax outputBuffer,U lda #STDOUT lbsr PRINT_NULL_STRING ; close paths and exit lbra CLOSE_EXIT MAINLOOP_NOW_CONNECTED lda <refreshFlag beq MAINLOOP_NOW_CONNECTED_STDOUT ; if here, we are in graphics mode and need to update status text leax strGfxRetrieving,PCR lbsr PRINT_GFX_STATUS bra MAINLOOP_SETUP_WX_REQUEST MAINLOOP_NOW_CONNECTED_STDOUT lda #STDOUT leax strConnectSuccess,PCR lbsr PRINT_NULL_STRING MAINLOOP_SETUP_WX_REQUEST clr <pendingConnect clr <copyContentFlag clr <contentLengthFlag ; send the weather HTTP request to the server lbsr SEND_WEATHER_REQUEST ; check for any more data/response from the network lbra MAINLOOP_NEXT_READ MAINLOOP_NEW_DATA ; first copy new data into our main buffer from temporary one leax networkBuffer,U ldy <replyBufferEndPtr sty <replyBufferCurPtr ; this now will become our new current ptr after copying ldb <dwReadBytes+1 MAINLOOP_NEW_DATA_COPY_NEXT lda ,X+ sta ,Y+ decb bne MAINLOOP_NEW_DATA_COPY_NEXT sty <replyBufferEndPtr ; save end position in reply buffer for future data to continue at sty <searchEndPtr lda <copyContentFlag beq MAINLOOP_CHECK_HEADER_STUFF ; subtract new bytes we just copied in from total remaining of the main content and keep copying ldd <contentLength subd <dwReadBytes std <contentLength lbhi MAINLOOP_NEXT_READ ; if we have not reached 0 (or less) bytes reamining, keep copying lbra PROCESS_WEATHER_DATA MAINLOOP_CHECK_HEADER_STUFF lda <contentLengthFlag lbne MAINLOOP_NEW_DATA_FIND_BODY ; before going any further, retrieve the HTTP server reply code to see if we have a valid ; reply from the server, or some kind of error like "Bad Reqest" of "Not Found" leay strKEYWORDhttp1.1,PCR ldx <replyBufferCurPtr lbsr STRING_SEARCH_BUFFER lbcs MAINLOOP_NEXT_READ ; if here, we found the HTTP reply version. check the reply code and handle accordingly lbsr FIND_NEXT_NONSPACE_CHAR ldd #"40" cmpd ,X bne MAINLOOP_CHECK_HEADER_VALID_REQUEST cmpa 2,X lbeq ERROR_LOCATION_NOT_FOUND lbra ERROR_UNKNOWN_SERVER_RESPONSE MAINLOOP_CHECK_HEADER_VALID_REQUEST ldd #"20" cmpd ,X lbne ERROR_UNKNOWN_SERVER_RESPONSE cmpb 2,X lbne ERROR_UNKNOWN_SERVER_RESPONSE ; if here, we have a valid HTTP response code 200 ; now search for the content length information so we know how much weather data to read in leay strKEYWORDcontent,PCR ldx <replyBufferCurPtr lbsr STRING_SEARCH_BUFFER lbcs MAINLOOP_NEXT_READ ; if here, we found the content length number for HTTP reply inc <contentLengthFlag ; set flag so next time through, we will search for content body ; parse and copy content length value and convert to it's actual value ldb #5 ; limit value to 5 chars (4 for numbers + 1 CR) MAINLOOP_CONTENT_LENGTH_FIND_CR lda ,X+ cmpa #C$CR beq MAINLOOP_CONTENT_LENGTH_FOUND_CR decb bne MAINLOOP_CONTENT_LENGTH_FIND_CR MAINLOOP_CONTENT_LENGTH_FOUND_CR ; now convert the ascii numbers into our actual content length value ldy #0 clra leax -1,X ; undo auto-increment ldb ,-X ; get singles place subb #'0' ; convert to value leay D,Y ; add value to our total ldb ,-X cmpb #C$SPAC ; see if we reached beginning of content-length (and end of our calc) beq MAINLOOP_CONTENT_LENGTH_DONE subb #'0' lda #10 ; 10's mul leay D,Y ldb ,-X cmpb #C$SPAC ; see if we reached beginning of content-length (and end of our calc) beq MAINLOOP_CONTENT_LENGTH_DONE subb #'0' lda #100 ; 100's mul leay D,Y ldb ,-X cmpb #C$SPAC ; see if we reached beginning of content-length (and end of our calc) beq MAINLOOP_CONTENT_LENGTH_DONE subb #'0' ; 1000's is a little trickier for multiplication lda #10 mul lda #100 mul leay D,Y ; never going to be more than 9999 bytes so skip finding higher multiples of 10 MAINLOOP_CONTENT_LENGTH_DONE sty <contentLength sty <constContentLength MAINLOOP_NEW_DATA_FIND_BODY ; now find end of header (and start of actual body/content of HTTP request) leay strKEYWORDdoubleCRLF,PCR ldx <replyBufferCurPtr lbsr STRING_SEARCH_BUFFER lbcs MAINLOOP_NEXT_READ ; if here, we found start of content body. now continously read the rest of the server reply from dw ; until we reach the number of bytes in contentLength stx <contentBodyPtr ; subtract the number of valid content bytes left in remainder of buffer from total content length count ldd <replyBufferEndPtr subd <contentBodyPtr std <tempWord ldd <contentLength subd <tempWord std <contentLength inc <copyContentFlag ; tell mainloop that from now on, just copy data until we run out lbra MAINLOOP_NEXT_READ PROCESS_WEATHER_DATA ; disable timeout timer since we got valid request and read all data lda <nilPath bmi PROCESS_WEATHER_DATA_SKIP_VRN ldb #SS.FClr ldx #0 ldy #0 os9 I$SetStt PROCESS_WEATHER_DATA_SKIP_VRN lbsr PARSE_JSON_WEATHER_DATA ; check if location wasn't found by searching for location name "Tong Not, Vietnam" because that is ; what shows up for some reason when it cant find the location leax jsonAreaName,U leay strKEYWORDtong,PCR lbsr COMPARE_PARAM bcs PROCESS_WEATHER_DATA_VALID_AREA_FOUND lda jsonRegion,U lbeq ERROR_LOCATION_NOT_FOUND PROCESS_WEATHER_DATA_VALID_AREA_FOUND lda <refreshFlag bne PROCESS_WEATHER_DATA_SKIP_STDOUT ; tell user we successfully finished retreiving data and found valid result lda #STDOUT leax strDone,PCR lbsr PRINT_NULL_STRING PROCESS_WEATHER_DATA_SKIP_STDOUT DEBUG_WTTR_DOWN leax jsonPressure,U lbsr CONVERT_HPA_TO_INHG lda <outputMode lbeq OUTPUT_GRAPHICS_MODE ; ok user wants text-only output. build our formatted output string from the data leay outputBuffer,U leax strCurrentWeather,PCR lbsr STRING_COPY_RAW leax jsonAreaName,U lbsr STRING_COPY_RAW ldd #", " std ,Y++ leax jsonRegion,U lbsr STRING_COPY_RAW ldd #": " std ,Y++ leax jsonWeatherDesc,U lbsr STRING_COPY_RAW leax strTemperature,PCR lbsr STRING_COPY_RAW lda <metricOutputFlag bne TEXT_SHOW_METRIC_TEMP leax jsonTempF,U ldb #'F' bra TEXT_TEMP_STORE TEXT_SHOW_METRIC_TEMP leax jsonTempC,U ldb #'C' TEXT_TEMP_STORE lbsr STRING_COPY_RAW lda <charDegreesSymbol std ,Y++ leax strFeelsLike,PCR lbsr STRING_COPY_RAW lda <metricOutputFlag bne TEXT_SHOW_METRIC_FEELS_LIKE leax jsonFeelsLikeF,U ldb #'F' bra TEXT_FEELS_LIKE_STORE TEXT_SHOW_METRIC_FEELS_LIKE leax jsonFeelsLikeC,U ldb #'C' TEXT_FEELS_LIKE_STORE lbsr STRING_COPY_RAW lda <charDegreesSymbol std ,Y++ leax strHumidity,PCR lbsr STRING_COPY_RAW leax jsonHumidity,U lbsr STRING_COPY_RAW lda #'%' sta ,Y+ leax strPressure,PCR lbsr STRING_COPY_RAW ;leax jsonPressure,U lda <metricOutputFlag bne TEXT_SHOW_METRIC_PRESSURE leax pressureInches,U lbsr STRING_COPY_RAW leax strPressureInHg,PCR bra TEXT_PRESSURE_STORE TEXT_SHOW_METRIC_PRESSURE leax jsonPressure,U lbsr STRING_COPY_RAW leax strPressurehPa,PCR TEXT_PRESSURE_STORE lbsr STRING_COPY_RAW leax strWind,PCR lbsr STRING_COPY_RAW leax jsonWindDir,U lbsr STRING_COPY_RAW ldd #" a" std ,Y++ ldd #"t " std ,Y++ lda <metricOutputFlag bne TEXT_SHOW_METRIC_WIND_SPEED leax jsonWindSpeedMPH,U lbsr STRING_COPY_RAW leax strWindMPH,PCR bra TEXT_WIND_SPEED_STORE TEXT_SHOW_METRIC_WIND_SPEED leax jsonWindSpeedKPH,U lbsr STRING_COPY_RAW leax strWindKPH,PCR TEXT_WIND_SPEED_STORE lbsr STRING_COPY_RAW leax strPrecipitation,PCR lbsr STRING_COPY_RAW lda <metricOutputFlag bne TEXT_SHOW_METRIC_RAIN leax jsonPrecipInches,U lbsr STRING_COPY_RAW lda #C$SPAC sta ,Y+ ldd #"in" std ,Y++ bra TEXT_RAIN_DONE TEXT_SHOW_METRIC_RAIN leax jsonPrecipMM,U lbsr STRING_COPY_RAW lda #C$SPAC sta ,Y+ ldd #"mm" std ,Y++ TEXT_RAIN_DONE leax strDateTime,PCR lbsr STRING_COPY_RAW leax jsonLocalTime,U lbsr STRING_COPY_RAW ldd #cr_lf std ,Y++ clr ,Y lda #STDOUT leax outputBuffer,U lbsr PRINT_NULL_STRING lbra CLOSE_EXIT OUTPUT_GRAPHICS_MODE ; check if we are displaying gfx weather for first time or if this is a refresh of existing data lda <refreshFlag lbne DISPLAY_REFRESHED_GRAPHICS ; tell the user we are about to load the graphics assets from disk lda #STDOUT leax strMsgLoadingGfx,PCR lbsr PRINT_NULL_STRING ; do some initial setup for gfx display the first time through ; get process ID that was assigned to us os9 F$ID sta groupID,U ; setup a path to create new graphics window lda #UPDAT. clrb leax winPathName,PCR os9 I$Open sta gfxWindowPath,U ; now setup the window params etc leax dwSetWindow,PCR ldy #dwSetWindowSz os9 I$Write lbsr SET_KEYBOARD_RAW ; setup raw IO mode for keyboard input for after weather is displayed lbsr LOAD_ALL_GFX_DIGITS ; load all the graphics digits into all their GET/PUT buffers ; setup the colors for text leay outputBuffer,U ldd #$1B32 std ,Y++ lda #7 sta ,Y+ ldd #$1B33 std ,Y++ lda #16 ; same as a 0 without interferring with string nulls sta ,Y+ leax outputBuffer,U lda gfxWindowPath,U lbsr PRINT_NULL_STRING ; ------ display title ------- lda gfxWindowPath,U leax selectFontTitle,PCR ldy #4 os9 I$Write lda #10 lbsr CHANGE_TEXT_COLOR lda #20 ; center text based on middle position of 40 column width screen clrb leax strGfxCoCoWX,PCR lbsr PRINT_NULL_STRING_CENTER_RELATIVE leax selectFontAuthor,PCR ldy #4 lda <gfxWindowPath os9 I$Write lda #11 lbsr CHANGE_TEXT_COLOR lda #20 ldb #2 leax strGfxAuthor,PCR lbsr PRINT_NULL_STRING_CENTER_RELATIVE DISPLAY_REFRESHED_GRAPHICS ; load appropriate weather conditions icon based on code from json data leax jsonWeatherCode,U lbsr LOAD_WX_CONDITIONS_ICON lbcs ERROR_GRAPHICS_MISSING bmi SKIP_ICON_SINCE_NOT_FOUND ; load the wind direction compass graphics leax jsonWindDir,U lbsr LOAD_WIND_ICON lbcs ERROR_GRAPHICS_MISSING lda <refreshFlag beq DISPLAY_REFRESHED_GRAPHICS_PRINT_STDOUT ; if here, we are processing a wx data refresh. erase previous weather graphics/info before ; displaying updated ones lda <gfxWindowPath leax screenUpdateSeq,PCR ; clears most of the screen (except for program name/author/etc) ldy #screenUpdateSeqSz os9 I$Write bra DISPLAY_REFRESHED_GRAPHICS_SKIP_STDOUT DISPLAY_REFRESHED_GRAPHICS_PRINT_STDOUT ; tell user graphics are done loading from disk lda #STDOUT leax strDone,PCR lbsr PRINT_NULL_STRING DISPLAY_REFRESHED_GRAPHICS_SKIP_STDOUT ; PUT graphics objects onto screen and then select whole screen making it visible ; first PUT the weather condition icon leax outputBuffer,U ldd #$1B2D ; PutBlk sequence std ,X++ lda groupID,U ldb #1 std ,X++ ldd #30 std ,X++ ldd #69 ; Y coords to put weather icon graphic std ,X++ ; lda <refreshFlag ; bne DWSELECT_SKIP ; TEMPORARY ; ldd #$1B21 ; DWSelect sequence ; std ,X++ ;DWSELECT_SKIP lda <gfxWindowPath leax outputBuffer,U ldy #8 os9 I$Write SKIP_ICON_SINCE_NOT_FOUND ; ---------- show location ------------- lda gfxWindowPath,U leax selectFontInfo,PCR ldy #4 os9 I$Write leay stringBuffer,U leax jsonAreaName,U lbsr STRING_COPY_RAW ldd #", " std ,Y++ leax jsonRegion,U lbsr STRING_COPY_RAW lda #7 lbsr CHANGE_TEXT_COLOR lda #27 ldb #5 leax stringBuffer,U lbsr PRINT_NULL_STRING_CENTER_RELATIVE ; ---------- show text conditions info ---------- lda #10 ldb #19 leax jsonWeatherDesc,U lbsr PRINT_NULL_STRING_CENTER_RELATIVE leay stringBuffer,U lda <metricOutputFlag bne GFX_SHOW_METRIC_TEMP leax jsonTempF,U ldb #'F' bra GFX_TEMP_STORE GFX_SHOW_METRIC_TEMP leax jsonTempC,U ldb #'C' GFX_TEMP_STORE lbsr STRING_COPY_RAW lda <charDegreesSymbol std ,Y++ clr ,Y leax stringBuffer,U ldd #120 ldy #72 lbsr PRINT_LARGE_DIGITS_STRING ; -------- show feels like temp ------- leay outputBuffer,U lda #$02 ; reposition text cursor code sta ,Y+ lda #$20+20 ldb #$20+15 std ,Y++ leax strGfxFeelsLike,PCR lbsr STRING_COPY_RAW leax infoColorSequence,PCR lbsr STRING_COPY_RAW lda <metricOutputFlag bne GFX_SHOW_METRIC_FEELS_LIKE leax jsonFeelsLikeF,U ldb #'F' bra GFX_FEELS_LIKE_STORE GFX_SHOW_METRIC_FEELS_LIKE leax jsonFeelsLikeC,U ldb #'C' GFX_FEELS_LIKE_STORE lbsr STRING_COPY_RAW lda #$BE std ,Y++ clr ,Y leax outputBuffer,U lda gfxWindowPath,U lbsr PRINT_NULL_STRING ; -------- show humidity --------- leay outputBuffer,U lda #$02 ; reposition text cursor code sta ,Y+ lda #$20+20 ldb #$20+17 std ,Y++ leax labelColorSequence,PCR lbsr STRING_COPY_RAW leax strGfxHumidity,PCR lbsr STRING_COPY_RAW leax infoColorSequence,PCR lbsr STRING_COPY_RAW leax jsonHumidity,U lbsr STRING_COPY_RAW lda #'%' clrb std ,Y++ leax outputBuffer,U lda gfxWindowPath,U lbsr PRINT_NULL_STRING ; ------- show pressure -------- leay outputBuffer,U lda #$02 sta ,Y+ lda #$20+20 ldb #$20+19 std ,Y++ leax labelColorSequence,PCR lbsr STRING_COPY_RAW leax strGfxPressure,PCR lbsr STRING_COPY_RAW leax infoColorSequence,PCR lbsr STRING_COPY_RAW lda <metricOutputFlag bne GFX_SHOW_METRIC_PRESSURE leax pressureInches,U lbsr STRING_COPY_RAW leax strPressureInHg,PCR bra GFX_PRESSURE_STORE GFX_SHOW_METRIC_PRESSURE leax jsonPressure,U lbsr STRING_COPY_RAW leax strPressurehPa,PCR GFX_PRESSURE_STORE lbsr STRING_COPY_RAW leax outputBuffer,U lda gfxWindowPath,U lbsr PRINT_NULL_STRING ; ------- show wind -------- leax outputBuffer,U ldd #$1B2D ; PutBlk sequence std ,X lda groupID,U ldb #20 ; 20 for wind dir GET/PUT buffer std 2,X ldd #243 std 4,X ldd #89 ; Y coords to put wind direction icon graphic std 6,X ; PUT graphic on the screen ldy #8 lda <gfxWindowPath os9 I$Write ; now do the text parts starting with the "Wind" label leay outputBuffer,U lda #$02 sta ,Y+ lda #$20+41 ldb #$20+9 std ,Y++ leax labelColorSequence,PCR lbsr STRING_COPY_RAW leax strGfxWind,PCR lbsr STRING_COPY_RAW lda #$02 sta ,Y+ lda #$20+40 ldb #$20+16 std ,Y++ lda <metricOutputFlag bne GFX_SHOW_METRIC_WIND_SPEED leax jsonWindSpeedMPH,U lbsr STRING_COPY_RAW leax strWindMPH,PCR bra GFX_WIND_SPEED_STORE GFX_SHOW_METRIC_WIND_SPEED leax jsonWindSpeedKPH,U lbsr STRING_COPY_RAW leax strWindKPH,PCR GFX_WIND_SPEED_STORE lbsr STRING_COPY_RAW ; now write the whole thing to screen leax outputBuffer,U lda <gfxWindowPath clrb lbsr PRINT_NULL_STRING ; ------ show rainfall -------------------- leay outputBuffer,U lda #$02 sta ,Y+ lda #$20+20 ldb #$20+21 std ,Y++ leax labelColorSequence,PCR lbsr STRING_COPY_RAW leax strGfxRainfall,PCR lbsr STRING_COPY_RAW leax infoColorSequence,PCR lbsr STRING_COPY_RAW lda <metricOutputFlag bne GFX_SHOW_METRIC_RAINFALL leax jsonPrecipInches,U lbsr STRING_COPY_RAW lda #C$SPAC sta ,Y+ ldd #"in" bra GFX_RAINFALL_STORE GFX_SHOW_METRIC_RAINFALL leax jsonPrecipMM,U lbsr STRING_COPY_RAW lda #C$SPAC sta ,Y+ ldd #"mm" GFX_RAINFALL_STORE std ,Y++ clr ,Y leax outputBuffer,U lda gfxWindowPath,U lbsr PRINT_NULL_STRING lda <refreshFlag bne GFX_SKIP_DWSELECT ; issue DWSelect to show the main window now lda <gfxWindowPath leax dwSelectSequence,PCR ldy #2 os9 I$Write GFX_SKIP_DWSELECT ; ------ show obervation date/time -------- leay stringBuffer,U leax labelColorSequence,PCR lbsr STRING_COPY_RAW leax strGfxObsTime,PCR lbsr STRING_COPY_RAW leax jsonLocalTime,U lbsr STRING_COPY_RAW leax stringBuffer,U lbsr PRINT_GFX_STATUS ; and we are done! now close the current open DW path since we'll lose it anyways when the HTTP server ; closes connection lda <networkPath os9 I$Close ; mark the path flag as unopened lda #$FF sta <networkPath clra sta <networkDataReady sta <pendingConnect sta <networkTimeoutFlag ; enable auto-refresh timer lda <nilPath ldb #SS.FSet ; code $C7 ldx #36000 ; 10 minutes ldy #0 ldu #wx_refresh_signal os9 I$SetStt ldu <uRegImage ; setup the signal for keyboard input to check for ENTER press for manual refresh of wx data clr <keyInputFlag lda <gfxWindowPath ldb #SS.SSig ldx #keyboard_signal os9 I$SetStt lbra MAINLOOP REFRESH_WEATHER_DATA_MANUALLY ; we first have to disable the regular refresh timer since user requested an immediate manual update lda <nilPath ldb #SS.FClr ldx #0 ldy #0 os9 I$SetStt REFRESH_WEATHER_DATA ; inform user we are about to request updated weather for the place they initially requested clr <wxRefreshFlag leax strGfxConnecting,PCR lbsr PRINT_GFX_STATUS ; let the program know that everything was already set it up once and just updates from now on lda #1 sta <refreshFlag lbra SETUP_REFRESH_WEATHER_REQUEST DISPLAY_INFO_USAGE lda #STDOUT leax strUsage,PCR ldy #strUsageSz os9 I$Write bra CLOSE_EXIT ERROR_UNKNOWN_SERVER_RESPONSE ; X should still be pointed to server error code in the HTTP reply header stx <tempPtr leay outputBuffer,U leax strErrorServerReply,PCR lbsr STRING_COPY_RAW ldx <tempPtr lbsr STRING_COPY_CR lda #')' ldb #C$CR std ,Y ldd #$0A00 std 2,Y leax outputBuffer,U bra CLOSE_EXIT_PRINT_STDOUT ERROR_INVALID_PARAMS leax strErrorInvalidParam,PCR bra CLOSE_EXIT_PRINT_STDOUT ERROR_UNKNOWN_FLAG leax strErrorInvalidFlag,PCR bra CLOSE_EXIT_PRINT_STDOUT ERROR_TIMEOUT_EXIT lda <refreshFlag beq ERROR_TIMEOUT_EXIT_STDOUT ; if here, we are in graphics output mode so tell them request timed out leax strGfxTimeout,PCR lbsr PRINT_GFX_STATUS bra CLOSE_EXIT ERROR_TIMEOUT_EXIT_STDOUT leax strErrorTimeout,PCR bra CLOSE_EXIT_PRINT_STDOUT ERROR_NO_DRIVEWIRE leax strErrorNoDrivewire,PCR bra CLOSE_EXIT_PRINT_STDOUT ERROR_LOCATION_NOT_FOUND leay outputBuffer,U leax strErrorLocation,PCR lbsr STRING_COPY_RAW ldx <shellParamPtr lbsr STRING_COPY_CR lda #')' ldb #C$CR std ,Y ldd #$0A00 std 2,Y leax outputBuffer,U bra CLOSE_EXIT_PRINT_STDOUT ERROR_GRAPHICS_MISSING leax strErrorGraphics,PCR bra CLOSE_EXIT_PRINT_STDOUT USER_ABORT_EXIT lda <gfxWindowPath bmi USER_ABORT_EXIT_SKIP_GFX_CLOSE os9 I$Close USER_ABORT_EXIT_SKIP_GFX_CLOSE leax strUserAborted,PCR CLOSE_EXIT_PRINT_STDOUT lda #STDOUT lbsr PRINT_NULL_STRING CLOSE_EXIT lda <gfxWindowPath bmi CLOSE_EXIT_SKIP_GFX os9 I$Close CLOSE_EXIT_SKIP_GFX lda <nilPath bmi CLOSE_EXIT_SKIP_VRN os9 I$Close CLOSE_EXIT_SKIP_VRN lda <networkPath bmi PROGRAM_EXIT ; if no path was ever opened, skip the close call os9 I$Close PROGRAM_EXIT clrb os9 F$Exit ; -------------------------------------------------------------------- ; signal handler ; -------------------------------------------------------------------- SIGNAL_HANDLER cmpb #network_signal beq SIGNAL_HANDLER_NETWORK cmpb #connect_timeout_signal beq SIGNAL_HANDLER_NETWORK_TIMEOUT cmpb #keyboard_signal beq SIGNAL_HANDLER_KEYBOARD cmpb #wx_refresh_signal beq SIGNAL_HANDLER_WX_REFRESH cmpb #S$Intrpt beq SIGNAL_HANDLER_ABORT cmpb #S$Abort beq SIGNAL_HANDLER_ABORT rti SIGNAL_HANDLER_NETWORK inc <networkDataReady rti SIGNAL_HANDLER_NETWORK_TIMEOUT inc <networkTimeoutFlag rti SIGNAL_HANDLER_KEYBOARD inc <keyInputFlag rti SIGNAL_HANDLER_WX_REFRESH inc <wxRefreshFlag rti SIGNAL_HANDLER_ABORT inc <abortFlag rti ; -------------------------------------------------------------------- ; setup drivewire server network paths ; -------------------------------------------------------------------- DRIVEWIRE_SETUP pshs Y,X,D lda #UPDAT. leax networkPathName,PCR os9 I$Open bcs DRIVEWIRE_SETUP_EXIT sta <networkPath ldb #SS.Opt leax pdBuffer,U os9 I$GetStt bcs DRIVEWIRE_SETUP_EXIT ; switch to RAW mode instead now leax PD.UPC-PD.OPT,X ldb #PD.QUT-PD.UPC DRIVEWIRE_SETUP_RAW_LOOP clr ,X+ decb bpl DRIVEWIRE_SETUP_RAW_LOOP lda <networkPath ldb #SS.Opt leax pdBuffer,U os9 I$SetStt bcs DRIVEWIRE_SETUP_EXIT ; setup the initial network signal lda <networkPath ldb #SS.SSig ldx #network_signal os9 I$SetStt DRIVEWIRE_SETUP_EXIT ; carry will already be set if error or clear if not puls D,X,Y,PC ; ------------------------------------------------------------------- ; check if drivewire server has new data for us and read it if we do ; ------------------------------------------------------------------- DRIVEWIRE_GET_DATA pshs X ; check if we have data to read before trying to do it lda <networkPath ldb #SS.Ready os9 I$GetStt bcs DRIVEWIRE_GET_DATA_ERROR clra tfr D,Y lda <networkPath leax networkBuffer,U os9 I$Read bcs DRIVEWIRE_GET_DATA_ERROR tfr Y,D leax D,X stx <networkBufferEndPtr std <dwReadBytes DRIVEWIRE_GET_DATA_ERROR puls X,PC ; ---------------------------------------------------------------------- SET_KEYBOARD_RAW pshs X,D lda <gfxWindowPath ldb #SS.Opt leax pdBuffer,U os9 I$GetStt bcs SET_KEYBOARD_RAW_EXIT ; switch to RAW mode leax PD.UPC-PD.OPT,X ldb #PD.INT-PD.UPC SET_KEYBOARD_RAW_LOOP clr ,X+ decb bpl SET_KEYBOARD_RAW_LOOP lda <gfxWindowPath ldb #SS.Opt leax pdBuffer,U os9 I$SetStt SET_KEYBOARD_RAW_EXIT puls D,X,PC ; ---------------------------------------------------------------------- PRINT_BYTE_HEX pshs U,Y,X,D ;ldu <uRegImage leax asciiHexList,PCR leay strHexWord,U lda #'$' sta ,Y lda <u8Value lsra lsra lsra lsra lda A,X sta 1,Y ; store first digit lda <u8Value anda #$0F lda A,X clrb std 2,Y ; store second digit leax strHexWord,U lbsr PRINT_NULL_STRING puls D,X,Y,U,PC ; -------------------------------------------------------------------- ; Entry: X = pointer to string to print as status update ; -------------------------------------------------------------------- PRINT_GFX_STATUS pshs Y,X,D ; move cursor to status area, clear that line, copy in text from entry pointer, and display it leay outputBuffer,U lda #$02 sta ,Y+ lda #$20+7 ldb #$20+24 std ,Y++ lda #$03 sta ,Y+ lda #$02 sta ,Y+ ldd #$2744 std ,Y++ ; X should still contain pointer to string to display lbsr STRING_COPY_RAW lda <gfxWindowPath leax outputBuffer,U lbsr PRINT_NULL_STRING puls D,X,Y,PC ; -------------------------------------------------------------------- SEND_WEATHER_REQUEST pshs Y,X,D leay outputBuffer,U leax strGetPrefix,PCR lbsr STRING_COPY_RAW ldx <shellParamPtr lbsr LOCATION_COPY leax strWeatherFormatTxt,PCR lbsr STRING_COPY_RAW leax strGetSuffix,PCR lbsr STRING_COPY_RAW leax strHostInfo,PCR lbsr STRING_COPY_RAW leax strUserAgent,PCR lbsr STRING_COPY_RAW leax outputBuffer,U lbsr FIND_LEN_UNTIL_EOF lda <networkPath os9 I$Write puls D,X,Y,PC ; ---------------------------------------------------------------------- PARSE_JSON_WEATHER_DATA pshs U,Y,X,D ; make sure U contains usual os9 pointer to data area of vars leay jsonKeywordCurCond,PCR ldx <contentBodyPtr lbsr SEARCH_JSON_KEYWORD lbcs PARSE_JSON_WEATHER_DATA_ERROR stx <jsonCurCondPtr lda #json_keyword_vars_sz sta <jsonTableCounter leau jsonKeywordVarsTable,PCR PARSE_JSON_WEATHER_DATA_NEXT_ENTRY ldx <jsonCurCondPtr ldd ,U++ leay 0,PCR leay D,Y lbsr SEARCH_JSON_KEYWORD bcc PARSE_JSON_WEATHER_DATA_FOUND_PARAM ; since we couldnt find that variable, load and copy blank/empty entry leax strBlank,PCR PARSE_JSON_WEATHER_DATA_FOUND_PARAM ; now get pointer to destination variable to copy to for corresponding table entry ldy 6,S ; load pointer to data area for os9 vars on stack in U ldd ,U++ leay D,Y lbsr COPY_JSON_VARIABLE dec <jsonTableCounter bne PARSE_JSON_WEATHER_DATA_NEXT_ENTRY ; now copy the more complicated parameters with multiple layers of elements ldu 6,S ; restore normal os9 data area ptr from stack leay strJSONweatherDesc,PCR ldx <jsonCurCondPtr lbsr SEARCH_JSON_KEYWORD bcs PARSE_JSON_WEATHER_DATA_ERROR leay strJSONvalue,PCR lbsr SEARCH_JSON_KEYWORD bcs PARSE_JSON_WEATHER_DATA_ERROR leay jsonWeatherDesc,U lbsr COPY_JSON_VARIABLE ; grab the area name text which has proper capitalization etc leay strJSONnearestArea,PCR ldx <jsonCurCondPtr lbsr SEARCH_JSON_KEYWORD bcs PARSE_JSON_WEATHER_DATA_ERROR stx <tempPtr ; save ptr to start of "Nearest Area" section leay strJSONareaName,PCR lbsr SEARCH_JSON_KEYWORD bcs PARSE_JSON_WEATHER_DATA_ERROR leay strJSONvalue,PCR lbsr SEARCH_JSON_KEYWORD bcs PARSE_JSON_WEATHER_DATA_ERROR leay jsonAreaName,U lbsr COPY_JSON_VARIABLE ; now the state or region leay strJSONregion,PCR ldx <tempPtr lbsr SEARCH_JSON_KEYWORD bcs PARSE_JSON_WEATHER_DATA_ERROR leay strJSONvalue,PCR lbsr SEARCH_JSON_KEYWORD bcs PARSE_JSON_WEATHER_DATA_ERROR leay jsonRegion,U lbsr COPY_JSON_VARIABLE PARSE_JSON_WEATHER_DATA_DONE puls D,X,Y,U,PC PARSE_JSON_WEATHER_DATA_ERROR puls D,X,Y,U,PC ; -------------------------------------------------------------------------- SEARCH_JSON_KEYWORD pshs Y,X,D SEARCH_JSON_KEYWORD_OPEN_NEXT ; find an open-quote lbsr FIND_NEXT_QUOTE_CHAR bcs SEARCH_JSON_KEYWORD_FAILED SEARCH_JSON_KEYWORD_FOUND_OPEN_QUOTE ldy 4,S ; reset ptr to keyword we are trying to match clrb SEARCH_JSON_KEYWORD_CHECK_NEXT lda ,Y+ beq SEARCH_JSON_KEYWORD_CHECK_TERMINATOR cmpa ,X+ bne SEARCH_JSON_KEYWORD_JUMP_NEXT decb bne SEARCH_JSON_KEYWORD_CHECK_NEXT SEARCH_JSON_KEYWORD_FAILED orcc #1 puls D,X,Y,PC SEARCH_JSON_KEYWORD_JUMP_NEXT ; something didnt match so first find the close-quote of current mismatched word so we can ; start finding open-quote of next keyword leax -1,X ; undo auto increment lbsr FIND_NEXT_QUOTE_CHAR bcc SEARCH_JSON_KEYWORD_OPEN_NEXT ; found close-quote. go search for new open quote now for next keyword bra SEARCH_JSON_KEYWORD_FAILED SEARCH_JSON_KEYWORD_CHECK_TERMINATOR lda ,X+ cmpa #$22 ; check for close-quote to confirm the keyword matches exactly with no extra chars left bne SEARCH_JSON_KEYWORD_JUMP_NEXT ; nope, jump to search for new json keyword to try lda ,X+ cmpa #':' bne SEARCH_JSON_KEYWORD_FAILED ; syntax doesnt match so fail lbsr FIND_NEXT_NONSPACE_CHAR ; if here, we successfully matched out keyword. save new ptr to stack and return stx 2,S andcc #$FE puls D,X,Y,PC ; ------------------------------------------------------------------------ ; Entry: X = pointer to start of json parameter value ; Y = destination to copy to ; Exit: on success, Y = pointer to null terminator. ; ----------------------------------------------------------------------- COPY_JSON_VARIABLE pshs X,D lbsr FIND_NEXT_QUOTE_CHAR ; found start of the element. copy until terminating quote mark ldb #32 ; variable should never be more than 32 chars (protect against overflow) COPY_JSON_VARIABLE_COPY_NEXT lda ,X+ cmpa #$22 beq COPY_JSON_VARIABLE_DONE sta ,Y+ decb bne COPY_JSON_VARIABLE_COPY_NEXT ; if here, overflow happened COPY_JSON_VARIABLE_ERROR orcc #1 puls D,X,PC COPY_JSON_VARIABLE_DONE clr ,Y andcc #$FE puls D,X,PC ; ------------------------------------------------------------------------------ ; Convert pressure in hPa to american units inHg. HUGE THANKS to David Philipsen ; for writing the actual math parts! ; Entry: X = pointer to ascii string with hPa number to convert ; Exit: v24h, v24m, v24l will be populated with 24 bit integer representing inHg ; the first 2 numbers are the real integers but rest are decimals ; ------------------------------------------------------------------------------ CONVERT_HPA_TO_INHG pshs Y,X,D ldd #0 std <hpaValue ; find end of number in hpa and convert ascii to real value CONVERT_HPA_TO_INHG_FIND_END lda ,X+ bne CONVERT_HPA_TO_INHG_FIND_END ; do single digit lda ,--X suba #'0' sta <hpaValue+1 ; do 10s digit lda ,-X suba #'0' ldb #10 mul addd <hpaValue std <hpaValue ; do 100s digit lda ,-X suba #'0' ldb #100 mul addd <hpaValue std <hpaValue cmpx 2,S bls CONVERT_HPA_TO_INHG_GOT_VALUE ; do 1000s digit if exists lda ,-X suba #'0' ldb #100 mul lda #10 mul addd <hpaValue std <hpaValue CONVERT_HPA_TO_INHG_GOT_VALUE *** HUGE THANKS to David Philipsen for writing this conversion routine!! *** ldd #0 std v24h,U sta v24l,U ; clear 24-bit accumulator ldd <hpaValue ; get hPa value subd #1016 ; normalize to base of 1016 std <hpaValue ; (Todd) replace hpaValue with normalized number to use at the end std <tempWord std v24m,U ; add the value to accum (1x) bcc CONVERT_HPA_TO_INHG_NO_CARRY_SK0 dec v24h,U CONVERT_HPA_TO_INHG_NO_CARRY_SK0 addd v24h,U ; add the value to accum (256x) std v24h,U ldd <tempWord ; get original value aslb rola ; mult orig val by 2 std <tempWord ; save it addd v24h,U std v24h,U ; add the value to accum (512x) ldd <tempWord aslb rola aslb rola std <tempWord ; now shifted two more for 8x addd v24m,U std v24m,U ; add the value to the accum (8x) ldd <tempWord addd v24h,U ; add the value to accum (2048x) std v24h,U ldd <hpaValue asra rorb addd v24h,U std v24h,U ; add the value to accum (128x) ; total now in accumulator is 2953x of the original value ldd #$C7EC ; add in 3,000,300 addd v24m,U std v24m,U bcc CONVERT_HPA_TO_INHG_NO_CARRY_SK4 inc v24h,U CONVERT_HPA_TO_INHG_NO_CARRY_SK4 lda #$2D adda v24h,U sta v24h,U ; convert result to ascii and copy to variable leax v24h,U leay pressureInches,U lbsr CONVERT_BINARY32_DECIMAL puls D,X,Y,PC ; ------------------------------------- ; convert 32 bit value to comma delimited decimal number ; Entry: X = pointer to 24 bit value to convert ; Y = pointer to destination to write ASCII result ; Exit: ; ------------------------------------- CONVERT_BINARY32_DECIMAL pshs U,Y,X,D leay ,X leax u32Value,U clr ,X lda ,Y sta 1,X ldd 1,Y std 2,X ldu 4,S ; load ptr in Y on stack into U register ldd #"00" std ,U std 3,U lda #'.' sta 2,U clr 5,U leay bin32dec1M,PCR CONVERT_BINARY32_DECIMAL_NEXT_1M lbsr SUBTRACT_32BIT bcs CONVERT_BINARY32_DECIMAL_DO_100K inc ,U bra CONVERT_BINARY32_DECIMAL_NEXT_1M CONVERT_BINARY32_DECIMAL_DO_100K lbsr ADD_32BIT leay bin32dec100K,PCR CONVERT_BINARY32_DECIMAL_NEXT_100K lbsr SUBTRACT_32BIT bcs CONVERT_BINARY32_DECIMAL_DO_10K inc 1,U bra CONVERT_BINARY32_DECIMAL_NEXT_100K CONVERT_BINARY32_DECIMAL_DO_10K lbsr ADD_32BIT leay bin32dec10K,PCR CONVERT_BINARY32_DECIMAL_NEXT_10K lbsr SUBTRACT_32BIT bcs CONVERT_BINARY32_DECIMAL_DO_1K inc 3,U bra CONVERT_BINARY32_DECIMAL_NEXT_10K CONVERT_BINARY32_DECIMAL_DO_1K lbsr ADD_32BIT ldd 2,X CONVERT_BINARY32_DECIMAL_NEXT_1K subd #1000 bcs CONVERT_BINARY32_DECIMAL_DO_100 inc 4,U bra CONVERT_BINARY32_DECIMAL_NEXT_1K CONVERT_BINARY32_DECIMAL_DO_100 addd #1000 clr <tempByte CONVERT_BINARY32_DECIMAL_NEXT_100 subd #100 bcs CONVERT_BINARY32_DECIMAL_DO_ROUNDING inc <tempByte bra CONVERT_BINARY32_DECIMAL_NEXT_100 CONVERT_BINARY32_DECIMAL_DO_ROUNDING lda <tempByte cmpa #5 blo CONVERT_BINARY32_DECIMAL_DO_ROUNDING_DONE inc 4,U lda 4,U cmpa #'9' bls CONVERT_BINARY32_DECIMAL_DO_ROUNDING_DONE lda #'0' sta 4,U inc 3,U lda 3,U cmpa #'9' bls CONVERT_BINARY32_DECIMAL_DO_ROUNDING_DONE lda #'0' sta 3,U inc 1,U lda 1,U cmpa #'9' bls CONVERT_BINARY32_DECIMAL_DO_ROUNDING_DONE lda #'0' sta 1,U inc ,U CONVERT_BINARY32_DECIMAL_DO_ROUNDING_DONE puls D,X,Y,U,PC ; ----------------------------------- ; 32 bit subtraction ; ----------------------------------- SUBTRACT_32BIT pshs D ldd 2,X subd 2,Y std 2,X ldd ,X sbcb 1,Y sbca ,Y std ,X ; carry should be set properly now from subtract, now make sure zero flag works too ;ldd ,X ; not needed cuz previous instruction was STD ,X which already sets the Z and N flags bne SUBTRACT_32BIT_NOT_ZERO ldd 2,X andcc #%11110111 SUBTRACT_32BIT_NOT_ZERO puls D,PC ;---------------------------------- ; 32 bit addition ; --------------------------------- ADD_32BIT pshs D ldd 2,X addd 2,Y std 2,X ldd ,X adcb 1,Y adca ,Y std ,X bne ADD_32BIT_NOT_ZERO ldd 2,X andcc #%11110111 ADD_32BIT_NOT_ZERO puls D,PC include string_management.asm include graphics_management.asm ************************************************************************************* EMOD MODULE_SIZE ; put this at the end so it can be used for module size