From df98441f3a298e64bf9f61c3d6e0c917ccd49e2b Mon Sep 17 00:00:00 2001 From: Mark Fisher Date: Wed, 18 Sep 2024 16:18:57 +0100 Subject: [PATCH] 4.7.0 add fn_clock interface and atari/apple2 cc65 implementations --- Changelog.md | 4 ++ apple2/src/bus/cc65/sp_find_device.s | 22 +++---- apple2/src/bus/cc65/sp_find_fuji.s | 14 ++--- apple2/src/bus/cc65/sp_read.s | 3 - apple2/src/fn_clock/cc65/clock_get_time.s | 70 +++++++++++++++++++++++ apple2/src/fn_clock/cc65/clock_get_tz.s | 45 +++++++++++++++ apple2/src/fn_clock/cc65/clock_set_tz.s | 55 ++++++++++++++++++ atari/src/fn_clock/clock_get_time.s | 54 +++++++++++++++++ atari/src/fn_clock/clock_get_tz.s | 65 +++++++++++++++++++++ atari/src/fn_clock/clock_set_tz.s | 42 ++++++++++++++ fujinet-clock.h | 58 +++++++++++++++++++ fujinet-clock.inc | 5 ++ fujinet-fuji.h | 6 +- makefiles/build.mk | 4 ++ version.txt | 2 +- 15 files changed, 421 insertions(+), 28 deletions(-) create mode 100644 apple2/src/fn_clock/cc65/clock_get_time.s create mode 100644 apple2/src/fn_clock/cc65/clock_get_tz.s create mode 100644 apple2/src/fn_clock/cc65/clock_set_tz.s create mode 100644 atari/src/fn_clock/clock_get_time.s create mode 100644 atari/src/fn_clock/clock_get_tz.s create mode 100644 atari/src/fn_clock/clock_set_tz.s create mode 100644 fujinet-clock.h create mode 100644 fujinet-clock.inc diff --git a/Changelog.md b/Changelog.md index 5cc079a..61d576f 100644 --- a/Changelog.md +++ b/Changelog.md @@ -2,6 +2,10 @@ ## [Unreleased] +## [4.7.0] - 2024-09-20 + +- [clock] a simple clock library to interface with FujiNet clock device with atari/apple2 (cc65) implementations + ## [4.6.2] - 2024-09-15 - [apple2 bus] refactor all sp_get_xxx functions, moving common code into sp_find_device diff --git a/apple2/src/bus/cc65/sp_find_device.s b/apple2/src/bus/cc65/sp_find_device.s index 6511e88..afc0374 100644 --- a/apple2/src/bus/cc65/sp_find_device.s +++ b/apple2/src/bus/cc65/sp_find_device.s @@ -2,7 +2,7 @@ .export device_type_id .export device_count - .export device_id_idx + .export _device_id_idx .export tmp_orig_type .import _sp_cmdlist @@ -15,12 +15,11 @@ .import pusha .import negax .import return0 - .import return1 .include "sp.inc" .include "zp.inc" -; bool sp_find_device(); +; uint8_t sp_find_device(); ; device_type_id contains the type id to search for ; where type_id is the internal fujinet device type_id: ; $10 = fujinet @@ -69,7 +68,7 @@ have_count: sta device_count lda #$01 - sta device_id_idx + sta _device_id_idx device_loop: jsr pusha ; the current Device ID lda #$03 @@ -84,12 +83,12 @@ device_loop: ; found it, so return the current index ldx #$00 - lda device_id_idx + lda _device_id_idx rts not_found_yet: - inc device_id_idx - lda device_id_idx + inc _device_id_idx + lda _device_id_idx cmp device_count bcc device_loop beq device_loop @@ -123,9 +122,11 @@ id_loc1 = *-2 beq not_found - ; found it from searching, so return success after setting the id + ; found it from searching, so return the id after setting it jsr set_id - jmp return1 + ldx #$00 + cmp #$00 ; set status registers for the return based on A's id value + rts not_found: tax ; set x to 0 @@ -136,9 +137,8 @@ id_loc2 = *-2 rts - .bss device_type_id: .res 1 device_count: .res 1 -device_id_idx: .res 1 +_device_id_idx: .res 1 tmp_orig_type: .res 1 \ No newline at end of file diff --git a/apple2/src/bus/cc65/sp_find_fuji.s b/apple2/src/bus/cc65/sp_find_fuji.s index 2f0baa8..0428aa6 100644 --- a/apple2/src/bus/cc65/sp_find_fuji.s +++ b/apple2/src/bus/cc65/sp_find_fuji.s @@ -6,14 +6,10 @@ .import _sp_payload .import _sp_status - .import device_id_idx + .import _device_id_idx .import device_count .import pusha - .import return0 - .import return1 - - .macpack cpu ; uint8_t sp_get_fuji_id() _sp_get_fuji_id: @@ -35,7 +31,7 @@ not_found_by_id: ; assumes device_count is set from previous search by ID try_fallback: lda #$01 - sta device_id_idx + sta _device_id_idx device_loop: jsr pusha ; the current Device ID @@ -63,12 +59,12 @@ device_loop: ; found it, so return the current index ldx #$00 - lda device_id_idx + lda _device_id_idx bne :+ not_found_yet: - inc device_id_idx - lda device_id_idx + inc _device_id_idx + lda _device_id_idx cmp device_count bcc device_loop beq device_loop diff --git a/apple2/src/bus/cc65/sp_read.s b/apple2/src/bus/cc65/sp_read.s index c3691b0..d34c88a 100644 --- a/apple2/src/bus/cc65/sp_read.s +++ b/apple2/src/bus/cc65/sp_read.s @@ -2,10 +2,7 @@ .import _sp_cmdlist .import sp_dispatch - .import _sp_nw_unit - .import _sp_payload - .import popa .import sp_rw_common .include "sp.inc" diff --git a/apple2/src/fn_clock/cc65/clock_get_time.s b/apple2/src/fn_clock/cc65/clock_get_time.s new file mode 100644 index 0000000..a481375 --- /dev/null +++ b/apple2/src/fn_clock/cc65/clock_get_time.s @@ -0,0 +1,70 @@ + .export _clock_get_time + + .import _sp_count + .import _sp_get_clock_id + .import _sp_payload + .import _sp_status + + .import incsp2 + .import popax + .import pusha + .import return0 + .import return1 + + .include "macros.inc" + .include "zp.inc" + +; uint8_t clock_get_time(uint8_t* time_data, TimeFormat format); + +_clock_get_time: + sta time_format ; format, save it where we need it! saves a BSS byte + + ; get the device id of the clock, this is stored in _sp_clock_id, but also returned so we can check if it failed (0 is error) + jsr _sp_get_clock_id + bne got_id + + ; no clock found, return 1 as an error (FN_ERR_IO_ERROR) + ; but first adjust the stack to remove the data pointer +error: + jsr incsp2 + jmp return1 + +got_id: + ; call sp_status(uint8_t dest, uint8_t statcode) + sta tmp1 ; save the clock device id + + ; convert the time format to the appropriate device specific code. + ; SIMPLE_BINARY (0) -> 'T' + ; PRODOS_BINARY (1) -> 'P' + ; APETIME_TZ_BINARY (2) -> 'A' + ; APETIME_BINARY (3) -> 'B' + ; TZ_ISO_STRING (4) -> 'S' + ; UTC_ISO_STRING (5) -> 'Z' + + ldx #$00 +time_format = *-1 + ; ensure the value is valid + cpx #$06 + bcs error + pusha tmp1 + lda code_table, x + + jsr _sp_status + bne error + + ; results are in sp_payload, the clock data returned is small (4 to 26 bytes) + ; _sp_count holds the number of bytes to copy from sp_payload, will always contain at least 1 byte (null terminator) + popax ptr1 ; read the time_data location into ptr1 + ldy #$00 +: lda _sp_payload, y + sta (ptr1), y + iny + cpy _sp_count + bne :- + + jmp return0 + +.data +code_table: + .byte 'T', 'P', 'A', 'B', 'S', 'Z' + diff --git a/apple2/src/fn_clock/cc65/clock_get_tz.s b/apple2/src/fn_clock/cc65/clock_get_tz.s new file mode 100644 index 0000000..133d0f7 --- /dev/null +++ b/apple2/src/fn_clock/cc65/clock_get_tz.s @@ -0,0 +1,45 @@ + .export _clock_get_tz + + .import _sp_count + .import _sp_get_clock_id + .import _sp_payload + .import _sp_status + + .import pusha + .import return0 + .import return1 + + .include "macros.inc" + .include "zp.inc" + +; uint8_t clock_get_tz(uint8_t* tz); + +_clock_get_tz: + axinto tmp_tz_loc + jsr _sp_get_clock_id + bne got_id + +error: + jmp return1 + +got_id: + ; sp_status(clock_id, 'G') + jsr pusha + lda #'G' + jsr _sp_status + bne error + + ; copy sp_count bytes from payload into buffer, there's always at least 1 byte (null terminator) + mwa tmp_tz_loc, ptr1 + ldy #$00 +: lda _sp_payload, y + sta (ptr1), y + iny + cpy _sp_count + bne :- + + jmp return0 + + +.bss +tmp_tz_loc: .res 2 diff --git a/apple2/src/fn_clock/cc65/clock_set_tz.s b/apple2/src/fn_clock/cc65/clock_set_tz.s new file mode 100644 index 0000000..f6b4d5c --- /dev/null +++ b/apple2/src/fn_clock/cc65/clock_set_tz.s @@ -0,0 +1,55 @@ + .export _clock_set_tz + + .import _fn_error + .import _memcpy + .import _sp_control + .import _sp_get_clock_id + .import _sp_payload + .import _strlen + + .import incsp2 + .import pusha + .import pushax + .import return1 + + .include "macros.inc" + .include "zp.inc" + +; uint8_t clock_set_tz(char *tz); +_clock_set_tz: + axinto tmp_tz_ptr ; save the tz + + ; get the device id of the clock, this is stored in _sp_clock_id, but also returned so we can check if it failed (0 is error) + jsr _sp_get_clock_id + bne got_id + + ; no clock found, return 1 as an error (FN_ERR_IO_ERROR) + jmp return1 + +got_id: + jsr pusha ; store the destination device for call to sp_control + + ; copy timezone string into sp_payload, including the null terminator + pushax #(_sp_payload + 2) + pushax tmp_tz_ptr + jsr _strlen + + clc + adc #$01 + bcc :+ + inx + +: sta _sp_payload + 0 + stx _sp_payload + 1 + jsr _memcpy + + ; destination was stored on s/w stack already + lda #'T' ; set 'T'imezone + jsr _sp_control + + ; convert to fujinet error + jmp _fn_error + + +.bss +tmp_tz_ptr: .res 2 \ No newline at end of file diff --git a/atari/src/fn_clock/clock_get_time.s b/atari/src/fn_clock/clock_get_time.s new file mode 100644 index 0000000..0d37348 --- /dev/null +++ b/atari/src/fn_clock/clock_get_time.s @@ -0,0 +1,54 @@ + .export _clock_get_time + + .import _bus + .import _fuji_success + + .import popax + .import return0 + + .include "device.inc" + .include "fujinet-clock.inc" + .include "macros.inc" + .include "zp.inc" + +; uint8_t clock_get_time(uint8_t* time_data, TimeFormat format); +_clock_get_time: + tay + cpy #$06 ; was the format value in range? + bcc ok + + ; return an error status + jmp return0 + +ok: + lda t_clock_get_time_cmd, y + sta IO_DCB::dcomnd + lda t_clock_get_time_len, y + sta IO_DCB::dbytlo + popax IO_DCB::dbuflo + +; the SIO clock device follows the APETIME device ID: +; #define SIO_DEVICEID_APETIME 0x45 + mva #SIO_CLOCK_DEVICE_ID, IO_DCB::ddevic + mva #$40, IO_DCB::dstats + ldx #$00 + stx IO_DCB::dunuse + stx IO_DCB::daux1 + stx IO_DCB::daux2 + stx IO_DCB::dbythi + inx ; x = 1 + stx IO_DCB::dunit + inx ; x = 2 - as this is a FN non network call, we can keep this low + stx IO_DCB::dtimlo + jsr _bus + jmp _fuji_success + +.rodata + +; tables for the commands that have to be sent for the different types of time command +; see fuji_clock.h +t_clock_get_time_cmd: + .byte 'T', 'P', SIO_APETIMECMD_GETTZTIME, SIO_APETIMECMD_GETTIME, 'S', 'Z' + +t_clock_get_time_len: + .byte 7, 4, 6, 6, 25, 25 diff --git a/atari/src/fn_clock/clock_get_tz.s b/atari/src/fn_clock/clock_get_tz.s new file mode 100644 index 0000000..c1fea53 --- /dev/null +++ b/atari/src/fn_clock/clock_get_tz.s @@ -0,0 +1,65 @@ + .export _clock_get_tz + + .import _bus + .import _fuji_success + + .import popax + .import return0 + + .include "device.inc" + .include "fujinet-clock.inc" + .include "macros.inc" + .include "zp.inc" + +; uint8_t clock_get_tz(uint8_t* tz); +_clock_get_tz: + axinto tmp_tz_loc ; save the tz buffer while we get the length of system tz + + ; get the current tz length into "tmp_len" + mva #SIO_CLOCK_DEVICE_ID, IO_DCB::ddevic + mva #'L', IO_DCB::dcomnd + mva #$40, IO_DCB::dstats ; read + ldx #$00 + stx IO_DCB::dunuse + stx IO_DCB::daux1 + stx IO_DCB::daux2 + stx IO_DCB::dbythi + inx ; x = 1 + stx IO_DCB::dunit + stx IO_DCB::dbytlo ; only 1 byte to read + inx ; x = 2 - as this is a FN non network call, we can keep this low + stx IO_DCB::dtimlo + mwa #tmp_len, IO_DCB::dbuflo + jsr _bus + + ; check if we had a success (dstats holds status result) + jsr _fuji_success + bne ok + + ; error return, we weren't able to find the length of the current timezone + rts + +ok: + ; fetch "tmp_len" bytes from the FN for the tz string + mwa tmp_tz_loc, IO_DCB::dbuflo ; tz buffer into dbuf + mva #'G', IO_DCB::dcomnd ; Get system TZ string + mva tmp_len, IO_DCB::dbytlo ; string length of timezone + mva #$40, IO_DCB::dstats ; read + + ;; non of these have changed since previous BUS call + ; ldx #$00 + ; stx IO_DCB::dunuse + ; stx IO_DCB::daux1 + ; stx IO_DCB::daux2 + ; stx IO_DCB::dbythi + ; inx ; x = 1 + ; stx IO_DCB::dunit + ; inx ; x = 2 - as this is a FN non network call, we can keep this low + ; stx IO_DCB::dtimlo + jsr _bus + jmp _fuji_success + + +.bss +tmp_len: .res 1 +tmp_tz_loc: .res 2 \ No newline at end of file diff --git a/atari/src/fn_clock/clock_set_tz.s b/atari/src/fn_clock/clock_set_tz.s new file mode 100644 index 0000000..a277b61 --- /dev/null +++ b/atari/src/fn_clock/clock_set_tz.s @@ -0,0 +1,42 @@ + .export _clock_set_tz + + .import _bus + .import _fuji_success + .import _strlen + + .import popax + + .include "device.inc" + .include "fujinet-clock.inc" + .include "macros.inc" + .include "zp.inc" + +; uint8_t clock_set_tz(char *tz); +_clock_set_tz: + axinto IO_DCB::dbuflo ; the TZ string to send, will be null terminated + jsr _strlen + + ; add 1 for the null terminator + clc + adc #$01 + bcc :+ + inx +: axinto IO_DCB::dbytlo + axinto IO_DCB::daux1 + + lda #SIO_APETIMECMD_SETTZ + sta IO_DCB::dcomnd + +; the SIO clock device follows the APETIME device ID: +; #define SIO_DEVICEID_APETIME 0x45 + + mva #SIO_CLOCK_DEVICE_ID, IO_DCB::ddevic + mva #$80, IO_DCB::dstats ; write mode for atari + ldx #$00 + stx IO_DCB::dunuse + inx ; x = 1 + stx IO_DCB::dunit + inx ; x = 2 + stx IO_DCB::dtimlo + jsr _bus + jmp _fuji_success diff --git a/fujinet-clock.h b/fujinet-clock.h new file mode 100644 index 0000000..c0a5850 --- /dev/null +++ b/fujinet-clock.h @@ -0,0 +1,58 @@ + +/** + * @brief FujiNet Clock Device Library + * @license gpl v. 3, see LICENSE for details. + */ + +#ifndef FUJINET_CLOCK_H +#define FUJINET_CLOCK_H + +#include + +// Read the error codes from network +#include "fujinet-network.h" + +// IF ADDITIONAL FORMATS ARE ADDED, DO NOT CHANGE THE CURRENT ORDER OF ENUMS +// UNLESS YOU REFACTOR clock_get_time() FUNCTIONS FOR THE PLATFORMS +// AS THEY RELY ON THE ORDER +typedef enum time_format_t { + // BINARY formats are just the numbers, not ascii characters for the number. + SIMPLE_BINARY, // 7 bytes: [Y(century, e.g. 20), Y(hundreds, e.g. 24), M(1-12), D(1-31), H(0-23), M(0-59), S(0-59)] - Uses the current FN Timezone + PRODOS_BINARY, // 4 bytes: special PRODOS format, see https://prodos8.com/docs/techref/adding-routines-to-prodos/ - Uses the current FN Timezone + APETIME_TZ_BINARY, // 6 bytes: [Day, Mon, Yr (YY), Hour, Min, Sec] - This version honours the Timezone either set in the WebUI or below in the clock_set_tz (both of which update the FN global timezone value) + APETIME_BINARY, // 6 bytes: [Day, Mon, Yr (YY), Hour, Min, Sec] - UTC version of the apetime data. This is for backwards compatibility for Atari Apetime with no TZ set. + + // STRING formats are full null terminated strings + TZ_ISO_STRING, // an ISO format: YYYY-MM-DDTHH:MM:SS+HHMM - Uses the current FN Timezone + UTC_ISO_STRING // Current UTC time, still ISO format, but with 0000 offset: YYYY-MM-DDTHH:MM:SS+HHMM + +} TimeFormat; + +/** + * @brief Set the FN clock's timezone + * @param tz the timezone string to apply + * @return fujinet-clock status/error code (See FN_ERR_* values) + */ +uint8_t clock_set_tz(char *tz); + +/** + * @brief Get the FN clock's timezone + * @param tz pointer to the receiving timezone buffer + * @return fujinet-clock status/error code (See FN_ERR_* values) + */ +uint8_t clock_get_tz(char *tz); + +/** + * @brief Get the current time in the format specified. + * @param time_data pointer to buffer for the response. This is uint8_t, but for STRING formats, will be null terminated and can be treated as a string. + * @param format a TimeFormat value to specify how the data should be returned. + * @return fujinet-clock status/error code (See FN_ERR_* values) + */ +uint8_t clock_get_time(uint8_t* time_data, TimeFormat format); + +#define SIO_APETIMECMD_GETTIME 0x93 +#define SIO_APETIMECMD_SETTZ 0x99 +#define SIO_APETIMECMD_GETTZTIME 0x9A + + +#endif // FUJINET_CLOCK_H \ No newline at end of file diff --git a/fujinet-clock.inc b/fujinet-clock.inc new file mode 100644 index 0000000..74fb326 --- /dev/null +++ b/fujinet-clock.inc @@ -0,0 +1,5 @@ +SIO_APETIMECMD_GETTIME := $93 +SIO_APETIMECMD_SETTZ := $99 +SIO_APETIMECMD_GETTZTIME := $9A + +SIO_CLOCK_DEVICE_ID := $45 diff --git a/fujinet-fuji.h b/fujinet-fuji.h index 0d5f773..01c75ff 100644 --- a/fujinet-fuji.h +++ b/fujinet-fuji.h @@ -559,15 +559,13 @@ bool fuji_hash_length(uint8_t mode); /////////////////////////////////////////////////////// // New hashing interface -enum HashType +typedef enum HashType { MD5, SHA1, SHA256, SHA512 -}; - -typedef enum HashType hash_alg_t; +} hash_alg_t; /** * @brief Returns the size of the hash that will be produced for the given hash_type and whether hex output is required or not. This should be used to calculate the memory needed for the output of \ref fuji_hash_data diff --git a/makefiles/build.mk b/makefiles/build.mk index 9b6e8c6..27ad3f2 100644 --- a/makefiles/build.mk +++ b/makefiles/build.mk @@ -69,6 +69,8 @@ FN_NW_HEADER = fujinet-network.h FN_NW_INC = fujinet-network.inc FN_FUJI_HEADER = fujinet-fuji.h FN_FUJI_INC = fujinet-fuji.inc +FN_CLOCK_HEADER = fujinet-clock.h +FN_CLOCK_INC = fujinet-clock.inc CHANGELOG = Changelog.md # allow for additional flags etc @@ -183,6 +185,8 @@ release: all | $(BUILD_DIR) $(DIST_DIR) cp $(FN_NW_INC) dist/ cp $(FN_FUJI_HEADER) dist/ cp $(FN_FUJI_INC) dist/ + cp $(FN_CLOCK_HEADER) dist/ + cp $(FN_CLOCK_INC) dist/ cp $(CHANGELOG) dist/ cd dist && zip fujinet-lib-$(CURRENT_TARGET)-$(VERSION_STRING).zip $(CHANGELOG) fujinet-$(CURRENT_TARGET)-$(VERSION_STRING).lib *.h *.inc $(call RMFILES,dist/fujinet-$(CURRENT_TARGET)-*.lib) diff --git a/version.txt b/version.txt index 3208b09..1163055 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -4.6.2 \ No newline at end of file +4.7.0 \ No newline at end of file