Unfinished Bitness

Finishing bits of technology, vintage and new.

  • About
  • Contact

Action! Graphics 0 Text Highlight

Posted by Ripdubski on 2022.06.30
Posted in: Atari, Programming. Tagged: 8 bit, Action!, Player/Missile. Leave a comment

In this post I show I used the Atari 8 bits Player/Missile graphics to produce a highlighter effect on graphics 0 screen text.

Essentially I am using players defined 8 bits wide and using double line resolution within the player/missile graphics. This equates to an 8×8 graphics 0 character cell with normal player width selected. I chose yellow as the highlight color as you might do on paper.

The only thing you need to watch out for is to ensure you don’t set the player luminance higher than the text luminance. If the player luminance is greater, you won’t be able to see the text behind it.

Using a player definition of 8 bits and combinations of player width of normal, double, and quadruple, you can highlight text at 2, 4, and 8 characters wide with a single player. With 4 players at quadruple width you can highlight 32 characters of the 40 character screen. If you want to use all 4 missiles as one player, you can achieve another 8 character highlight bringing it to the full 40 character screen width.

By placing players of different definitions (4 bits vs 8 bits wide) and player width sizes (single) next to each other you can highlight an odd number of characters.

Example

A screenshot of the end result:

The line labels on the right denote how each line was made:

“P0 0xFF Single” is player 0 formed from a 4 byte array consisting of $FF (all bits on) at single/normal player width to achieve 2 character coverage.

“P1 0xFF Double” is player 1 formed from a 4 byte array consisting of $FF (all bits on) at double player width to achieve 4 character coverage.

“P2 0xFF Quad” is player 2 formed from a 4 byte array consisting of $FF (all bits on) at quad player width to achieve 8 character coverage.

“P3 0x0F Single” is player 3 formed from a 4 byte array consisting of $0F (4 bits off, 4 bits on) at single player width to achieve 1 character coverage.

“M0 0x03 Double” is missile 0 formed from a 4 byte array consisting of $03 (bits 2 and 1 on) at double missile width to achieve 1 character coverage.

Code

The code is documented fairly well, and in combination with the description above, you should be able to see what is happening. If not, give me a shout. It does require the Action! Toolkit’s PMG.ACT and is coded for drive D2, change it if you need to.

; Prog..: PMHLTEXT.ACT
; Author: Ripdubski
; Desc..: Use P/M to highlight text
; Date..: 2022.06.29
; Notes.: Requires Action! Toolkit

INCLUDE "D2:PMG.ACT"

PROC main()       
  ; Player1 is 4 lines/rows of 11111111
  ; Player2 is 4 lines/rows of 00001111
  ; Missile is 4 lines/rows of 11
  BYTE ARRAY aPlyr1(4)=[$FF $FF $FF $FF],
             aPlyr2(4)=[$0F $0F $0F $0F],
             aMiss(4)=[$03 $03 $03 $03]
  BYTE bLp 

  ; Set graphics 0, already there.
  Graphics(0)

  ; Turn on P/M
  ; Double line res for 8 rows from 4 row array!
  PMGraphics(2)  
     
  ; For each player
  FOR bLp=0 TO 3
  DO               
    ; Clear data
    PMClear(bLp)

    ; Set color to yellow
    ; Luminance of 4 is less than text(8)
    PMColor(bLp,13,4)
  OD

  ; Clear needed missiles
  PMClear(4)
  
  ; Draw screen text
  Position(13,2)
  Print("Text Highlight")
  Position(21,5)
  Print("P0 0xFF Single")
  Position(21,7)
  Print("P1 0xFF Double")
  Position(21,9)
  Print("P2 0xFF Quad")
  Position(21,11)
  Print("P3 0x0F Single")
  Position(21,13)
  Print("M0 0x03 Double")

  FOR bLp=4 to 14
  DO 
    Position(10,bLp)
    Print("1234567890")
  OD                  

  ; Create each player in diff widths 
  ; Param 4 is the width (1=N/S,2=D,4=Q)
  ; Param 5 is the x position
  ; Param 6 is the y position
  PMCreate(0,aPlyr1,4,1,88,36)
  PMCreate(1,aPlyr1,4,2,88,44)
  PMCreate(2,aPlyr1,4,4,88,52)

  ; This player uses different array definition
  PMCreate(3,aPlyr2,4,1,116,60)

  ; Create used missiles (only 1 here)
  PMCreate(4,aMiss,4,2,88,68)

RETURN

There are many possibilities for using this technique.

Have fun!

Share this:

  • Click to email a link to a friend (Opens in new window)
  • Click to share on Facebook (Opens in new window)
  • Click to share on LinkedIn (Opens in new window)
  • Click to share on Pinterest (Opens in new window)
  • Click to share on Pocket (Opens in new window)
  • Click to share on Reddit (Opens in new window)
  • Click to share on Twitter (Opens in new window)

Like this:

Like Loading...

Modern MacOS App for Retro BBS’ing

Posted by Ripdubski on 2021.12.11
Posted in: BBS. Tagged: MacOS, MSDOS, Serial. Leave a comment

This is a quick write up on setting up Serial to call BBS’s. Serial by Decisive Tactics (https://www.decisivetactics.com) is a MacOS terminal app for, you guessed it, serial connections. It’s a great app I used quite a bit when working with the Atari Portfolio, the Atari ST, and to a lesser degree, the Atari 8 bits. The latest version of the app runs natively on M1 Macs as well!


First, set the main preferences (font, colors, cursor, etc). These will be used for all sessions. The important thing here is selecting a font that supports code page 437. This code page is common to DOS based PC’s in the U.S. There is a great resource for old PC fonts located here: https://int10h.org/oldschool-pc-fonts/ .

Here I am using the AC437 ACM VGA 9×14 font in 30 pt. I prefer this font because it lacks serifs. You’ll want ANSI colors turned on as well:

Next, Add a session. Use File/Open, click the + at the bottom left and choose New Remote Connection. Initially you just enter the connection type and address. Protocol will be Plain TCP Socket (when RAW is needed), then fill in the Host address.

Once both fields are populated the Advanced Settings button will be active. Click it! Start at the top item on the left and work your way down. First in the Name section, set the name as you want it to appear in the session list:

Then in the Remote Host section, you will want to clear the Username field:

There is nothing to do with Connection Info, skip to Pacing. No changes needed here, shown for reference:

Then in the Window section, set the window attributes. There is a choice of how to negotiate the window size (useful for *nix connections). Choose “set cli screen-length/width”, the size you want (80×25 most likely), and then check Locked:

Then in the Emulation section, set it to VT100, and the Text Encoding to Western (DOS Latis US/CP437):

Now click OK. You will see your entry in the Open Connection dialog in the Network section. You can create tags, and associate items with those tags by right clicking the item and selecting a tag. Then you can click the tag:

Now you can double click the entry to open it (aka dial), or highlight and click Open. If the BBS is not busy, you should get a connection. The TrueType font I chose and and at the point size I elected sometimes has minor render issues between character cells (seen here) – it doesn’t always do this and may be a result of the processing through all the ANSI to get the characters and color. It seems the more character changes, the more it happens:

When you logoff the BBS, the terminal will disconnect. Serial gives you a status in the upper right corner (Disconnected). It also shows “Connecting” in the same location during connection establishment. Note the lack of render issues (less character and color changes on this screen):

That’s it. Enjoy

Share this:

  • Click to email a link to a friend (Opens in new window)
  • Click to share on Facebook (Opens in new window)
  • Click to share on LinkedIn (Opens in new window)
  • Click to share on Pinterest (Opens in new window)
  • Click to share on Pocket (Opens in new window)
  • Click to share on Reddit (Opens in new window)
  • Click to share on Twitter (Opens in new window)

Like this:

Like Loading...

Splunking Apple Music Library

Posted by Ripdubski on 2021.02.21
Posted in: Apple, Audio. Tagged: Analysis, Apple Music, iTunes, Splunk. Leave a comment

Splunk is a fantastic data analytics tool.  I’ve been involved with it for the past few years at work.  Splunk allows you to download and run it freely with a limited ingest, which is perfect for home use and/or training.  I have it setup at home, primarily to learn, and sometimes to try new things.

Warning, this is a long post.

In the past I wrote a Javascript application called iStats, which I posted about a couple of times.  iStats was designed to provide play and library analytics for my iTunes (now Apple Music) library.  I was interested in seeing most played tracks, artists, and albums, and other statistics.  The previous blog posts for iStats are here:

Original: https://unfinishedbitness.info/2018/05/04/itunes-missing-feature-library-statistics/

Update: https://unfinishedbitness.info/2020/12/06/itunes-missing-feature-library-statistics-revised/

While iStats does exactly what I need, I was curious how Splunk could provide any additional insights as it has a far greater capability to analyze big data.  My Apple Music Library is an 18MB XML file when exported, with roughly 9,000 songs.  This post will demonstrate the searches and dashboard I built in Splunk to provide analytics on my library.  There are 22 searches with corresponding visual reports built into a single dashboard.

 

iStats Results

iStats produces output like this.  In the most recent update (linked above) I added the ability to export each track to CSV so I could pull the data into Elastic, another tool similar to Splunk.  I did create a dashboard in Elastic, but didn’t make the effort to blog about it.  The only comment I will make in comparing the two is that it was much easier to create the Splunk dashboard.

 

Splunk Dashboard

First, let’s see the Splunk dashboard.  It’s broken into 5 parts.  Open the images to see a larger version.  Each section has a title that describes what it represents:

 

Getting Data In To Splunk

To get the data into Splunk, I used the CSV export from my iStats program.  Note there is only one major section of the exported Apple Music library XML file that should be transposed into the CSV file – there is other stuff in the XML you don’t want to be part of the analysis.  Without iStats, you may not be able to fully replicate what I’ve done here with accurate results. If you don’t want to use iStats, fine, however you will need a similar CSV.  The required fields in the CSV are (the ones in quotes are alpha and quoted):

"Artist","Album","Track","Year","Genre",Plays,Skips,Size,Minutes,Seconds,Milliseconds,"Bitrate","Kind"

An example of the data:

"Candlebox","Candlebox","Far Behind","1993","Hard Rock",14,0,36579379,4,59,666,"971","Apple Lossless audio file"

The first step is to export the Apple Music library.  To export the library from Apple Music, from the File menu, use the Library menu item, and the Export Library… sub menu item.

The second step is to transform the exported XML file to CSV, omitting the items that are not a targeted part of the analysis.  This included movies/videos, voice memos, playlists, etc.  The target is only the songs.  iStats extracts only what is needed from the XML.  If you don’t use iStats, your mileage may vary.  Open iStats, ensure export to CSV is checked, select the exported XML file, and wait a few seconds.  Once the results are displayed, the CSV file should be saved as well.

Now that the CSV is created, it can be pulled into Splunk.  You’ll need to be logged into Splunk as a user with permissions to upload data.  If “Add Data” doesn’t show up, your user doesn’t have the proper permissions.  So, select Add Data:

 

Next, select Upload:

 

Next, select the CSV file.  This can be done using the picker or drag and drop:

 

Next, Splunk doesn’t understand how to define the timestamp, so it gets set to current.  This will mark all records with the current date and time:

 

Next, set the destination index.  If you want to use the searches I provide later, create and name the index “applemusic” (no spaces):

 

Next, review the selections, and click Submit:

 

Lastly, Splunk will show a summary and provide some options for exploring the newly imported data.  You can select “Start Searching”:

 

Going to the Search app will allow you to see the raw data:

 

Building the Dashboard

When building the dashboard, you do not need the elevated permissions required to import the data.  The single dashboard being built is called “Apple Music Statistics”.  It will have the description “Apple Music Library and Playtime Statistics”:

 

To build the dashboard, 22 searches will be created.  Note that I mis-spelled one here – it says “Aple” instead of “Apple”.  I didn’t notice until after capturing everything for this blog post.  Correct it as you go.  The searches are:

 

Building the Searches

I will build the searches in the order they appear on the dashboard as depicted at the top of this post (see Splunk Dashboard above).  Each of these will be created from the Search app.  Each search is listed with the Dashboard Section Name and the Search Name in parentheses:

Example: Dashboard Section Name (Search Name)

 

Library Kinds (Apple Music Kind)

index="applemusic" Kind!=Undefined Kind!="Purchased AAC audio file" 
| stats count by Kind 
| sort -count

 

Library Genres (Apple Music Genre)

index="applemusic" Kind!=Undefined Kind!="Purchased AAC audio file" 
| stats count by Genre 
| sort -count,Genre

 

Library Artist Count (Apple Music Library Artist Count)

index="applemusic" Artist != Compilation 
| dedup Artist 
| stats count

 

Library Album Count (Apple Music Library Album Count)

index="applemusic" 
| eval cd=Artist+" - "+Album
| dedup cd 
| stats count

 

Library Track Count (Apple Music Library Track Count)

index="applemusic" 
| stats count

 

Library Size in Bytes (Apple Music Library Size)

index="applemusic" 
| stats sum(Size) as s_size

 

Library Duration (Apple Music Library Duration)

index="applemusic" 
| stats sum(Minutes) as s_minutes sum(Seconds) as s_seconds sum(Milliseconds) as s_milliseconds 
| eval tot_seconds=(s_minutes*60)+s_seconds+floor(s_milliseconds/1000) 
| eval str_seconds=tostring(tot_seconds,"duration")
| rex field=str_seconds mode=sed "s/((((\d*)\+)?(\d*):)?(\d*):)?(\d*)/\4 days \5 hrs \6 mins \7 secs/"
| rex field=str_seconds mode=sed "s/^ days/0 days/"
| rename str_seconds as "Library Duration"
| table "Library Duration"

 

Playtime Duration (Apple Music Play Duration)

index="applemusic" 
| eval track_seconds=Plays*((Minutes*60)+Seconds+floor(Milliseconds/1000)) 
| stats sum(track_seconds) as play_seconds
| eval str_seconds=tostring(play_seconds,"duration")
| rex field=str_seconds mode=sed "s/((((\d*)\+)?(\d*):)?(\d*):)?(\d*)/\4 days \5 hrs \6 mins \7 secs/"
| rex field=str_seconds mode=sed "s/^ days/0 days/"
| rename str_seconds as "Play Duration"
| table "Play Duration"

 

Library Top 10 Artists by Track Count (Apple Music Library Artist Top 10)

index="applemusic" Artist != "Compilation" 
| top limit=10 Artist 
| rename count as Tracks 
| table Artist,Tracks

 

Library Top 10 Albums by Track Count (Apple Music Library Albums Top 10)

index="applemusic" 
| eval cd=Artist+" - "+Album 
| top 10 cd

 

Total Play Count (Apple Music Play Total)

index=applemusic 
| stats sum(Plays) as "Total Plays"

 

Total Skip Count (Apple Music Skip Total)

index=applemusic 
| stats sum(Skips) as "Total Skips"

 

Play Top 10 Artists (Apple Music Play Top 10 Artists)

index="applemusic" 
| stats sum(Plays) as "Play Count" by Artist 
| sort -"Play Count" 
| head 10 
| table Artist,"Play Count"

 

Play Top 10 Albums (Apple Music Play Top 10 Albums)

index="applemusic" 
| eval art_album=Artist + " - " + Album 
| stats sum(Plays) as "Play Count" by art_album 
| rename art_album as "Album Name" 
| sort -"Play Count" 
| head 10

 

Play Top 10 Tracks (Apple Music Play Top 10 Tracks)

index="applemusic" 
| sort -Plays 
| eval title = Artist + " - " + Track 
| table title,Plays 
| head 10

 

Skip Top 10 Artists (Apple Music Skip Top 10 Artists)

index="applemusic" 
| stats sum(Skips) as "Skip Count" by Artist 
| sort -"Skip Count" 
| head 10 
| table Artist,"Skip Count"

 

Skip Top 10 Albums (Apple Music Skip Top 10 Albums)

index="applemusic" 
| eval art_album=Artist + " - " + Album 
| stats sum(Skips) as "Skip Count" by art_album 
| rename art_album as "Album Name" 
| sort -"Skip Count" 
| head 10

 

Skip Top 10 Tracks (Apple Music Skip Top 10 Tracks)

index="applemusic" 
| sort -Skips 
| eval title = Artist + " - " + Track 
| table title,Skips 
| head 10

 

Library Top 10 Years (Apple Music Library Top 10 Years)

index="applemusic" 
| stats count as "Track Count" by Year 
| sort -"Track Count" 
| head 10

 

Play Top 10 Years (Apple Music Play Top 10 Years)

index="applemusic" 
| stats sum(Plays) as "Play Count" by Year 
| sort -"Play Count" 
| head 10

 

Library Top 10 Genres (Apple Music Library Top 10 Genre)

index="applemusic" 
| stats count as "Track Count" by Genre 
| sort -"Track Count" 
| head 10

 

Play Top 10 Genres (Apple Music Play Top 10 Genres)

index="applemusic" 
| stats sum(Plays) as "Play Count" by Genre 
| sort -"Play Count" 
| head 10

 

Dashboard Source

If your interested in the dashboard source XML, here it is.  You can see the colors set, chart options, etc.  Apologies the indenting was not preserved:

<dashboard theme="dark">
<label>Apple Music Statistics</label>
<description>Apple Music Library and Playtime Statistics</description>
<row>
<panel>
<chart>
<title>Library Kinds</title>
<search ref="Apple Music Kind"></search>
<option name="charting.drilldown">none</option>
</chart>
</panel>
<panel>
<chart>
<title>Library Genres</title>
<search ref="Apple Music Genre"></search>
<option name="charting.drilldown">none</option>
</chart>
</panel>
</row>
<row>
<panel>
<single>
<title>Library Artist Count</title>
<search>
<query>index="applemusic" Artist != Compilation | dedup Artist | stats count</query>
<earliest>0</earliest>
<sampleRatio>1</sampleRatio>
</search>
<option name="colorBy">value</option>
<option name="colorMode">none</option>
<option name="drilldown">none</option>
<option name="numberPrecision">0</option>
<option name="rangeColors">["0x53a051","0x0877a6","0xf8be34","0xf1813f","0xdc4e41"]</option>
<option name="rangeValues">[0,30,70,100]</option>
<option name="showSparkline">1</option>
<option name="showTrendIndicator">1</option>
<option name="trellis.enabled">0</option>
<option name="trellis.scales.shared">1</option>
<option name="trellis.size">medium</option>
<option name="trendColorInterpretation">standard</option>
<option name="trendDisplayMode">absolute</option>
<option name="unitPosition">after</option>
<option name="useColors">0</option>
<option name="useThousandSeparators">1</option>
</single>
</panel>
<panel>
<single>
<title>Library Album Count</title>
<search ref="Apple Music Library Album Count"></search>
<option name="drilldown">none</option>
</single>
</panel>
<panel>
<single>
<title>Library Track Count</title>
<search>
<query>index="applemusic" | stats count</query>
<earliest>0</earliest>
<sampleRatio>1</sampleRatio>
</search>
<option name="colorBy">value</option>
<option name="colorMode">none</option>
<option name="drilldown">none</option>
<option name="numberPrecision">0</option>
<option name="rangeColors">["0x53a051","0x0877a6","0xf8be34","0xf1813f","0xdc4e41"]</option>
<option name="rangeValues">[0,30,70,100]</option>
<option name="showSparkline">1</option>
<option name="showTrendIndicator">1</option>
<option name="trellis.enabled">0</option>
<option name="trellis.scales.shared">1</option>
<option name="trellis.size">medium</option>
<option name="trendColorInterpretation">standard</option>
<option name="trendDisplayMode">absolute</option>
<option name="unitPosition">after</option>
<option name="useColors">0</option>
<option name="useThousandSeparators">1</option>
</single>
</panel>
</row>
<row>
<panel>
<single>
<title>Library Size in Bytes</title>
<search>
<query>index="applemusic" 
| stats sum(Size) as s_size</query>
<earliest>0</earliest>
<sampleRatio>1</sampleRatio>
</search>
<option name="colorBy">value</option>
<option name="colorMode">none</option>
<option name="drilldown">none</option>
<option name="numberPrecision">0</option>
<option name="rangeColors">["0x53a051", "0x0877a6", "0xf8be34", "0xf1813f", "0xdc4e41"]</option>
<option name="rangeValues">[0,30,70,100]</option>
<option name="showSparkline">1</option>
<option name="showTrendIndicator">1</option>
<option name="trellis.enabled">0</option>
<option name="trellis.scales.shared">1</option>
<option name="trellis.size">medium</option>
<option name="trendColorInterpretation">standard</option>
<option name="trendDisplayMode">absolute</option>
<option name="unitPosition">after</option>
<option name="useColors">0</option>
<option name="useThousandSeparators">1</option>
</single>
</panel>
</row>
<row>
<panel>
<single>
<title>Library Duration</title>
<search ref="Apple Music Library Duration"></search>
<option name="drilldown">none</option>
</single>
</panel>
<panel>
<single>
<title>Playtime Duration</title>
<search ref="Apple Music Play Duration"></search>
<option name="colorMode">none</option>
<option name="drilldown">none</option>
<option name="rangeColors">["0x53a051","0x0877a6","0xf8be34","0xf1813f","0xdc4e41"]</option>
<option name="useColors">0</option>
</single>
</panel>
</row>
<row>
<panel>
<table>
<title>Library Top 10 Artists by Track Count</title>
<search>
<query>index="applemusic" Artist != "Compilation" | top limit=10 Artist | rename count as Tracks | table Artist,Tracks</query>
<earliest>0</earliest>
<sampleRatio>1</sampleRatio>
</search>
<option name="drilldown">none</option>
</table>
</panel>
<panel>
<table>
<title>Library Top 10 Albums by Track Count</title>
<search>
<query>index="applemusic" | eval cd=Artist+" - "+Album | top 10 cd | rename cd as "Album Name" | rename count as Tracks | table "Album Name",Tracks</query>
<earliest>0</earliest>
<latest></latest>
<sampleRatio>1</sampleRatio>
</search>
<option name="drilldown">none</option>
<option name="refresh.display">progressbar</option>
</table>
</panel>
</row>
<row>
<panel>
<single>
<title>Total Play Count</title>
<search>
<query>index=applemusic | stats sum(Plays) as "Total Plays"</query>
<earliest>0</earliest>
<sampleRatio>1</sampleRatio>
</search>
<option name="colorMode">none</option>
<option name="drilldown">none</option>
<option name="rangeColors">["0x53a051","0x0877a6","0xf8be34","0xf1813f","0x53a051"]</option>
<option name="useColors">1</option>
</single>
</panel>
<panel>
<single>
<title>Total Skip Count</title>
<search>
<query>index=applemusic | stats sum(Skips) as "Total Skips"</query>
<earliest>0</earliest>
<sampleRatio>1</sampleRatio>
</search>
<option name="drilldown">none</option>
<option name="rangeColors">["0x53a051","0x0877a6","0xf8be34","0xf1813f","0xdc4e41"]</option>
<option name="useColors">1</option>
</single>
</panel>
</row>
<row>
<panel>
<table>
<title>Play Top 10 Artists</title>
<search>
<query>index="applemusic" | stats sum(Plays) as "Play Count" by Artist | sort -"Play Count" | head 10 | table Artist,"Play Count"</query>
<earliest>0</earliest>
<sampleRatio>1</sampleRatio>
</search>
<option name="count">20</option>
<option name="dataOverlayMode">none</option>
<option name="drilldown">none</option>
<option name="percentagesRow">false</option>
<option name="rowNumbers">false</option>
<option name="totalsRow">true</option>
<option name="wrap">true</option>
<format type="color" field="Play Count">
<colorPalette type="minMidMax" maxColor="#53A051" minColor="#FFFFFF"></colorPalette>
<scale type="minMidMax"></scale>
</format>
<format type="number" field="Play Count">
<option name="precision">0</option>
</format>
</table>
</panel>
<panel>
<table>
<title>Play Top 10 Albums</title>
<search>
<query>index="applemusic" | eval art_album=Artist + " - " + Album | stats sum(Plays) as "Play Count" by art_album | rename art_album as "Album Name" | sort -"Play Count" | head 10</query>
<earliest>0</earliest>
<sampleRatio>1</sampleRatio>
</search>
<option name="count">20</option>
<option name="dataOverlayMode">none</option>
<option name="drilldown">none</option>
<option name="percentagesRow">false</option>
<option name="rowNumbers">false</option>
<option name="totalsRow">true</option>
<option name="wrap">true</option>
<format type="color" field="Play Count">
<colorPalette type="minMidMax" maxColor="#53A051" minColor="#FFFFFF"></colorPalette>
<scale type="minMidMax"></scale>
</format>
<format type="number" field="Play Count">
<option name="precision">0</option>
</format>
</table>
</panel>
<panel>
<table>
<title>Play Top 10 Tracks</title>
<search>
<query>index="applemusic" | sort -Plays | eval title = Artist + " - " + Track | table title,Plays | head 10</query>
<earliest>0</earliest>
<sampleRatio>1</sampleRatio>
</search>
<option name="count">20</option>
<option name="dataOverlayMode">none</option>
<option name="drilldown">none</option>
<option name="percentagesRow">false</option>
<option name="rowNumbers">false</option>
<option name="totalsRow">true</option>
<option name="wrap">true</option>
<format type="color" field="Plays">
<colorPalette type="minMidMax" maxColor="#53A051" minColor="#FFFFFF"></colorPalette>
<scale type="minMidMax"></scale>
</format>
<format type="number" field="Plays">
<option name="precision">0</option>
</format>
</table>
</panel>
</row>
<row>
<panel>
<table>
<title>Skip Top 10 Artists</title>
<search>
<query>index="applemusic" | stats sum(Skips) as "Skip Count" by Artist | sort -"Skip Count" | head 10 | table Artist,"Skip Count"</query>
<earliest>0</earliest>
<sampleRatio>1</sampleRatio>
</search>
<option name="count">20</option>
<option name="dataOverlayMode">none</option>
<option name="drilldown">none</option>
<option name="percentagesRow">false</option>
<option name="rowNumbers">false</option>
<option name="totalsRow">true</option>
<option name="wrap">true</option>
<format type="color" field="Skip Count">
<colorPalette type="minMidMax" maxColor="#DC4E41" minColor="#FFFFFF"></colorPalette>
<scale type="minMidMax"></scale>
</format>
<format type="number" field="Skip Count">
<option name="precision">0</option>
</format>
</table>
</panel>
<panel>
<table>
<title>Skip Top 10 Albums</title>
<search>
<query>index="applemusic" | eval art_album=Artist + " - " + Album | stats sum(Skips) as "Skip Count" by art_album | rename art_album as "Album Name" | sort -"Skip Count" | head 10</query>
<earliest>0</earliest>
<sampleRatio>1</sampleRatio>
</search>
<option name="count">20</option>
<option name="dataOverlayMode">none</option>
<option name="drilldown">none</option>
<option name="percentagesRow">false</option>
<option name="rowNumbers">false</option>
<option name="totalsRow">true</option>
<option name="wrap">true</option>
<format type="color" field="Skip Count">
<colorPalette type="minMidMax" maxColor="#DC4E41" minColor="#FFFFFF"></colorPalette>
<scale type="minMidMax"></scale>
</format>
<format type="number" field="Skip Count">
<option name="precision">0</option>
</format>
</table>
</panel>
<panel>
<table>
<title>Skip Top 10 Tracks</title>
<search>
<query>index="applemusic" | sort -Skips | eval title = Artist + " - " + Track | table title,Skips | head 10</query>
<earliest>0</earliest>
<sampleRatio>1</sampleRatio>
</search>
<option name="count">20</option>
<option name="dataOverlayMode">none</option>
<option name="drilldown">none</option>
<option name="percentagesRow">false</option>
<option name="rowNumbers">false</option>
<option name="totalsRow">true</option>
<option name="wrap">true</option>
<format type="color" field="Skips">
<colorPalette type="minMidMax" maxColor="#DC4E41" minColor="#FFFFFF"></colorPalette>
<scale type="minMidMax"></scale>
</format>
<format type="number" field="Skips">
<option name="precision">0</option>
</format>
</table>
</panel>
</row>
<row>
<panel>
<chart>
<title>Library Top 10 Years</title>
<search>
<query>index="applemusic" | stats count as "Track Count" by Year | sort -"Track Count" | head 10</query>
<earliest>0</earliest>
<sampleRatio>1</sampleRatio>
</search>
<option name="charting.chart">column</option>
<option name="charting.chart.showDataLabels">all</option>
<option name="charting.drilldown">none</option>
</chart>
</panel>
<panel>
<chart>
<title>Play Top 10 Years</title>
<search>
<query>index="applemusic" | stats sum(Plays) as "Play Count" by Year | sort -"Play Count" | head 10</query>
<earliest>0</earliest>
<sampleRatio>1</sampleRatio>
</search>
<option name="charting.chart">column</option>
<option name="charting.chart.showDataLabels">all</option>
<option name="charting.drilldown">none</option>
</chart>
</panel>
</row>
<row>
<panel>
<chart>
<title>Library Top 10 Genres</title>
<search>
<query>index="applemusic" | stats count as "Track Count" by Genre | sort -"Track Count" | head 10</query>
<earliest>0</earliest>
<sampleRatio>1</sampleRatio>
</search>
<option name="charting.chart">column</option>
<option name="charting.chart.showDataLabels">all</option>
<option name="charting.drilldown">none</option>
</chart>
</panel>
<panel>
<chart>
<title>Play Top 10 Genres</title>
<search>
<query>index="applemusic" | stats sum(Plays) as "Play Count" by Genre | sort -"Play Count" | head 10</query>
<earliest>0</earliest>
<sampleRatio>1</sampleRatio>
</search>
<option name="charting.chart">column</option>
<option name="charting.chart.showDataLabels">all</option>
<option name="charting.drilldown">none</option>
</chart>
</panel>
</row>
</dashboard>

That’s it for now.  As I add more to the dashboard, I’ll post about the additions.

 

 

Share this:

  • Click to email a link to a friend (Opens in new window)
  • Click to share on Facebook (Opens in new window)
  • Click to share on LinkedIn (Opens in new window)
  • Click to share on Pinterest (Opens in new window)
  • Click to share on Pocket (Opens in new window)
  • Click to share on Reddit (Opens in new window)
  • Click to share on Twitter (Opens in new window)

Like this:

Like Loading...

Action! String Padding

Posted by Ripdubski on 2021.02.13
Posted in: Atari, Programming. Tagged: 8 bit, Action!. Leave a comment

In the last post I showed a function to trim a string.  In this post I go the other way and demonstrate how to pad one.  This is useful when trying to print data in alignment.

The Function

This function was primarily written to left pad numbers.  As such there is a limit of 10 in length which is longer than the longest Action! number.  The code is again pretty well documented and will be including in the next release of my Action! library.  Here is short summary of how it works.

The procedure is called with 3 parameters.  The first is a pointer to a character array, which can not be a static string and must be a variable because the length will be altered.  The second parameter is the character you want to pad the string with.  Common values are space and zero.  The third parameter is the length to pad to (no more than 10).

First the procedure fills a temporary string with 10 of the desired pad character.  Next it copies the incoming string into the padded temporary string at the furthest right position to accommodate the length of the incoming string, essentially right justifying it.  Next the temporary padded string is copied back to the incoming string pointer, and the incoming string length is set to the specified pad length.

; --------------------------------------
; Proc..: StrPad(CHAR POINTER pS CHAR bc BYTE bL)
; Param.: pS=Pointer to string to pad
;         bC=Character to pad with
;         bL=Length to pad to
; Desc..: Left pads a string with a char
; Notes.: Max of 10 in length.
; --------------------------------------
PROC StrPad(CHAR POINTER pS CHAR bC BYTE bL)
; Declare a string filled with 10 spaces
CHAR ARRAY pA="          "

; Fill the temp string with desired char
; Use pA+1 which is the first character of the string.
; pA is the length of the string so skip over it.
SetBlock(pA+1, 10, bC)

; Copy incoming string into temp string
; Use desired length - incoming string length + 1 as position in temp string
; which puts incoming string at correct rightmost spot to accommodate its length.
SAssign(pA, pS, bL-pS(0)+1, bL)

; Copy newly padded temp string to incoming string
SCopy(pS, pA)

; Set incoming string pointer to new length
pS(0)=bL

RETURN

 

Sample Usage

Here is a short program demonstrating it usage.  Note that in this example the StrPad() function is not seen as its been incorporated into the LIBSTR.ACT file as part of my Action! library.  This will pad a byte, a card, and int to various lengths.

INCLUDE "D3:DEFINES.ACT"
INCLUDE "D3:LIBSTR.ACT"

PROC Main()
; Declare a byte, a card, an integer, and character array of 10 in length
BYTE bnum
CARD cnum
INT inum
CHAR ARRAY snum(10)

; Pad single character length byte to 2 positions with 0 character
bnum=1
; Convert the number into a string
StrB(bnum,snum)
; Call the pad function, passing the string, the pad character, and pad length
StrPad(snum, '0, 2)
; Show result
PrintF("Num: (2) [.....1]->[%S]%E",snum)

; Pad two character length byte to 4 positions with 0 character
bnum=21
; Convert the number into a string
StrB(bnum,snum)
; Call the pad function, passing the string, the pad character, and pad length
StrPad(snum, '0, 4)
; Show result
PrintF("Num: (4) [....21]->[%S]%E",snum)

; Pad five character length card to 5 positions with 0 character
cnum=64256
; Convert the number into a string
StrC(cnum,snum)
; Call the pad function, passing the string, the pad character, and pad length
StrPad(snum, '0, 5)
; Show result
PrintF("Num: (5) [.64256]->[%S]%E",snum)

; Pad five character length int to 8 positions with space character
inum=24768
; Convert the number into a string
StrI(inum,snum)
; Call the pad function, passing the string, the pad character, and pad length
StrPad(snum, 32, 8)
; Show result
PrintF("Num: (8) [.24768]->[%S]%E",snum)

; Pad six character length negative int to 8 positions with space character
inum=-24768
; Convert the number into a string
StrI(inum,snum)
; Call the pad function, passing the string, the pad character, and pad length
StrPad(snum, 32, 8)
; Show result
PrintF("Num: (8) [-24768]->[%S]%E",snum)

RETURN

 

Output

Here is the output showing that it works:

Share this:

  • Click to email a link to a friend (Opens in new window)
  • Click to share on Facebook (Opens in new window)
  • Click to share on LinkedIn (Opens in new window)
  • Click to share on Pinterest (Opens in new window)
  • Click to share on Pocket (Opens in new window)
  • Click to share on Reddit (Opens in new window)
  • Click to share on Twitter (Opens in new window)

Like this:

Like Loading...

Action! String Trimming

Posted by Ripdubski on 2021.02.13
Posted in: Atari, Programming. Tagged: 8 bit, Action!. Leave a comment

This post will demonstrate a function to trim a string.  By trim, I mean to remove all trailing spaces and stop at the first non space character.

 

The Function

Here is the function, which I’ve added to my Action! library.  A new version will be released soon that will include recent additions and many fixes.  It’s pretty well documented, but here is a short summary.

The procedure is called by passing a reference to a character array in (the string you want to trim).  This procedure will modify it directly, so it can not be a static string and must be a variable.  The procedure will start by setting a counter to the length of the incoming string.  It will move from the end to the beginning by decreasing this counter.  It examines the character at the counters position in the string.  If it’s a space, it sets the the new string length to the counter position minus one (to eliminate the space).  If it’s not a space, the procedure exits having found a termination point.   Each iteration through the loop, the counter is decremented by one to move one character left.

; -------------------------------------- 
; Proc..: StrTrim(CHAR POINTER ps)
; Param.: pS=Pointer to string to trim
; Desc  : Trims space from string end
; -------------------------------------- 
PROC StrTrim(CHAR POINTER pS)
BYTE bL,bC

; Set counter to end of string 
bL=pS(Ø)

; Loop from string end to start 
while bL>=1
DO
  ; If char is space, set len to curr-1 
  if pS(bL)=32 then
    bC=bL-1 
  else
    ; Char is not space, exit loop
    exit
  fi

  ; Decrease counter
  bL==-1 
OD

; Set string pointer length to new value 
pS(Ø)=bC

RETURN

 

Sample Usage

Here is a sample program demonstrating its use.  Note that the function has been incorporated into the file LIBSTR.ACT so you don’t see it in this source:

; Include library file that has StrTrim() in it.
INCLUDE "D3:LIBSTR.ACT"

MODULE

PROC Main()
; Declare character array of 20 in length
CHAR ARRAY cA(20)

; Copy some content in the char array defining all 20 chars
SCopy(cA,"12345 abc !         ")

; Print it to show a valid padded definition
PrintF("B4 : [%S]%E",cA)

; Call the trim function passing the char array pointer
StrTrim(cA)

; Print it again to show its been trimmed
PrintF("Aft: [%S]%E",cA)

RETURN

 

Output

Here is the output from the program showing it works:

 

Share this:

  • Click to email a link to a friend (Opens in new window)
  • Click to share on Facebook (Opens in new window)
  • Click to share on LinkedIn (Opens in new window)
  • Click to share on Pinterest (Opens in new window)
  • Click to share on Pocket (Opens in new window)
  • Click to share on Reddit (Opens in new window)
  • Click to share on Twitter (Opens in new window)

Like this:

Like Loading...

Action! DEFINE’ing

Posted by Ripdubski on 2021.02.12
Posted in: Atari, Programming. Tagged: 8 bit, Action!. Leave a comment

Over the course of my interactions with Action!, for whatever reason, I never had a need for defining a string value.  While numeric definitions are cake, string ones turned out not to be so much.

Let’s explore what I wanted to do.  I wanted to compare a variable that was input against a static string.  Why not just hard code the string?  Well, it is used in multiple locations, and having it DEFINEd makes it easy to change in all of them at once.  Thats the whole point of using DEFINE.  The source code I wanted to write:

if SCompare(sA, SSREADY) = 0 then

So using a define for SSREADY in an INCLUDEd source file accomplishes the goal of making it easy to change in just one place and use it multiple times across projects.  Something like this was needed for my code work:

DEFINE SSREADY = "TEST"

That works for numbers with no problem:

DEFINE NVAL = "100"

Anywhere that NVAL is found in the source, it will be replaced with 100.

So what is the issue?  Used in the context of SCompare above, it ends up producing code like this:

if SCompare(sA, TEST) = 0 then

Obviously thats not the intent of the source and it won’t work, and the compiler will tell you so immediately.  The compiler will think TEST is an undefined variable.  The word TEST needs double quotes around it.  Thats the goal.

Reading through the manual you will understand that in order to print double quotes, you have to precede each one with another.  Such as:

PrintE("Hello ""world""!")

which produces the output:

Hello "world"!

So moving back to the DEFINE statement it appears it needs to look like this:

DEFINE SSREADY = ""TEST""

But that doesn’t work either.  The compiler doesn’t like it.  The solution is to wrap the dual double quoted value in another set of double quotes.  But there is a caveat – they can not be all run together, or the compiler doesn’t understand which two of the 3 should be translated into a single double quote.  So you need to place a space between the DEFINE’s double quotes and the values double quote pair.  The proper syntax for DEFINEing a string value is:

DEFINE SSREADY = " ""TEST"" "

Here is a sample program that demonstrates this concept:

DEFINE NVAL = "100"
DEFINE SVAL = " ""TEST"" "

PROC Main()
PrintF("NVAL=[%B]%E",NVAL)
PrintF("SVAL=[%S]%E",SVAL)
RETURN

This will properly produce the output desired, and also allow for use in my original use case – SCompare().

Output:

Share this:

  • Click to email a link to a friend (Opens in new window)
  • Click to share on Facebook (Opens in new window)
  • Click to share on LinkedIn (Opens in new window)
  • Click to share on Pinterest (Opens in new window)
  • Click to share on Pocket (Opens in new window)
  • Click to share on Reddit (Opens in new window)
  • Click to share on Twitter (Opens in new window)

Like this:

Like Loading...

FujiNet Time in Action!

Posted by Ripdubski on 2021.02.03
Posted in: Atari, Programming. Tagged: 8 bit, Action!, APETIME, FujINet. 1 Comment

FujiNet is a fantastic new device for the Atari 8 bit line of computers which provides WiFi, and SD card storage among other things, all over SIO.  One of the functions provided is APETIME support, a protocol developed by AtariMax as part of their Atari Peripheral Emulator (APE).

In the last post I demonstrated my BASIC version of a program to get the time from the FujiNet device using APETIME. The procedure is documented here (https://github.com/FujiNetWIFI/fujinet-platformio/wiki/Accessing-the-Real-Time-Clock).  At the time of this writing there was no Action! example.

After completing the BASIC version, I moved to creating an Action! version.  This post details my solution.  I wrote the routine into a procedure which can be called from the main program at any point.  It only requires passing a pointer to a  6 byte array.

After failing to get success using the described 5 byte assembly procedure on the FujiNet wiki by using an inline assembly code block, I instead created a procedure with the name SIOV and setting its address to the OS SIO vector address.

The routine that gets the time from FujiNet is the FNGTime() procedure (FujiNet Get Time).  It first sets the DCB variables with their addresses set to the DCB (Device Control Block) memory location.  This is convenient for assigning (poking) values into those locations, with no need for the Poke function.

Next it sets up the SIO call by assigning the appropriate values into the DCB.  It sets the buffer location (DBUF), to the address of the byte array passed in.

Last it calls the SIO vector using the SIOV procedure.

The main routine sets aside 6 bytes of storage, calls the FNGTime() routine with the address of the storage array, then prints the result in a friendly fashion.

 

; Prog..: FNTIME.ACT
; Author: Wade Ripkowski
; Date..: 2021.02.01
; Desc..: Gets date/time from FujiNet

MODULE

; -----------------------------------
; Proc..: SIOV()
; Desc..: OS SIO Vector
; -----------------------------------
PROC SIOV=$E459()

; -----------------------------------
; Proc..: FNGTime(BYTE ARRAY bA)
; Desc..: Get date/time from FujiNet
; via APETIME protocol
; Return: 6 bytes DMYHMS into bA
; -----------------------------------
PROC FNGTime(BYTE POINTER bA)
; Set DCB variable pointers
BYTE DDEVIC=$300,
DUNIT=$301,
DCOMND=$302,
DSTATS=$303,
DTIMLO=$306
CARD DBUF=$304,
DBYT=$308

; Setup SIO call using byte array address
; by putting values into the DCB.
; APETime=Device 69 ($45), Unit 1
; Time command=147 ($93)
; Get 6 bytes, timeout just over 15s
DDEVIC=69
DUNIT=1
DCOMND=147
DSTATS=64
DTIMLO=15
DBUF=bA
DBYT=6

; Call SIO
SIOV()

RETURN

; -----------------------------------
; Main Routine
; -----------------------------------
PROC Main()
; Storage for 6 bytes preset to 0
BYTE ARRAY bDT(6)=[0 0 0 0 0 0]

; Call time routine with the storage array
FNGTime(bDT)

; What time is it?
PrintF("Date: 20%B.%B.%B%E",bDT(2),bDT(1),bDT(0))
PrintF("Time: %B:%B:%B%E",bDT(3),bDT(4),bDT(5))

RETURN

 

I’m going to include the FNGTime() function in my Action! library which was developed in 2016 and  released in early 2017 right here on this blog (search posts with Action! tag), and is now at version 1.5 and will be re-released soon as an update.

Share this:

  • Click to email a link to a friend (Opens in new window)
  • Click to share on Facebook (Opens in new window)
  • Click to share on LinkedIn (Opens in new window)
  • Click to share on Pinterest (Opens in new window)
  • Click to share on Pocket (Opens in new window)
  • Click to share on Reddit (Opens in new window)
  • Click to share on Twitter (Opens in new window)

Like this:

Like Loading...

FujiNet Time in BASIC

Posted by Ripdubski on 2021.02.03
Posted in: Atari, Programming. Tagged: 8 bit, APETIME, BASIC, FujINet. Leave a comment

FujiNet is a fantastic new device for the Atari 8 bit line of computers which provides WiFi, and SD card storage among other things, all over SIO.  One of the functions provided is APETIME support, a protocol developed by AtariMax as part of their Atari Peripheral Emulator (APE).

I wanted to write a function in Action! to get the time from the FujiNet device using APETIME, which is documented here (https://github.com/FujiNetWIFI/fujinet-platformio/wiki/Accessing-the-Real-Time-Clock). The procedure is straight forward and includes a BASIC example.  At the time of this writing there was no Action! example.

I examined the BASIC source and wasn’t happy with it’s length, specifically not liking the large IF block for the number padding.  I thought I could do something shorter and more efficient, and I wanted to fully understand the APETIME call before creating an Action! version.  In this post, I detail my BASIC program to get (and display) the date/time via APETIME from the FujiNet device.

In my version, I store the APETIME result in a string variable instead of page 6.  I also have eliminated the peek block by integrating those into a much shortened IF block that does inline 0 padding of the numbers.  For information on the Poke’s see the FujiNet wiki page (linked above).  Pokes for location 772 and 773 are the low and high bytes of the buffers string address in memory.

0 REM Program: FNTIME.BAS
1 REM Desc...: Gets time from FujiNet via APETIME using SIO
9 REM ----- Variables
10 DIM FNC$(5),BUF$(6),T$(2),YR$(4),MO$(2),DY$(2),HR$(2),MN$(2),SE$(2)
20 YR$="2000":MO$="00":DY$="00":HR$="00":MN$="00":SE$="00":LT=0
29 REM ----- Get address and MSB/LSB of string buffer
30 BA=ADR(BUF$):BH=INT(BA/256):BL=BA-(BH*256)
39 REM ----- Load assebly routine into function string
40 FOR I=1 TO 5:READ D:FNC$(I)=CHR$(D):NEXT I
99 REM ----- Setup SIO call for APETIME Get
100 POKE 768,69
105 POKE 769,1
110 POKE 770,147
115 POKE 771,64
120 POKE 772,BL:POKE 773,BH
125 POKE 774,15
130 POKE 776,6:POKE 777,0
199 REM ----- Execute assembly SIO call
200 X=USR(ADR(FNC$))
299 REM ----- Parse 6 byte result into strings
300 T$=STR$(PEEK(BA+2)):LT=LEN(T$)
305 YR$(5-LT)=T$
310 T$=STR$(PEEK(BA+1)):LT=LEN(T$)
315 MO$(3-LT)=T$
320 T$=STR$(PEEK(BA)):LT=LEN(T$)
325 DY$(3-LT)=T$
330 T$=STR$(PEEK(BA+3)):LT=LEN(T$)
335 HR$(3-LT)=T$
340 T$=STR$(PEEK(BA+4)):LT=LEN(T$)
345 MN$(3-LT)=T$
350 T$=STR$(PEEK(BA+5)):LT=LEN(T$)
355 SE$(3-LT)=T$
399 REM ----- Display results
400 ? "Date: ";YR$;".";MO$;".";DY$
410 ? "Time: ";HR$;":";MN$;":";SE$
990 END
999 REM ----- Data byte for assembly SIO jump
1000 DATA 104,32,89,228,96

In the next post I will show my Action! solution.

Share this:

  • Click to email a link to a friend (Opens in new window)
  • Click to share on Facebook (Opens in new window)
  • Click to share on LinkedIn (Opens in new window)
  • Click to share on Pinterest (Opens in new window)
  • Click to share on Pocket (Opens in new window)
  • Click to share on Reddit (Opens in new window)
  • Click to share on Twitter (Opens in new window)

Like this:

Like Loading...

Atari SpartaDOS Disk Images – Revisited

Posted by Ripdubski on 2021.02.03
Posted in: Atari. Tagged: 720K, 8 bit, Atari800MacX, ATR, Disk Image, SpartaDOS. 1 Comment

SpartaDOS is great MS-DOS like DOS (Disk Operating System) for the Atari 8 bit line of computers.  It was released on disk and eventually on cartridge too.  It received updates to allow hard drive access and boasted one of the best memory footprints, which is critical for these old 48K to 128K machines.  It also supports sub directories and timestamps.  It is my favorite Atari based DOS.  It’s still actively maintained today!

Atari started out at 90K disks (single sided single density 5.25″ floppy).  By comparison, a double sided single density 3.5″ floppy is 720K.  720K was a size never achieved with stock hardware throughout Atari’s existence.  This is a lot more room to work with.

In a previous post here (https://unfinishedbitness.info/2014/04/19/atari-spartados-disk-images/) I posted about creating a 720K ATR disk image with SpartaDOS on it.  Over the years I found that the specific sector size and configuration is problematic for some utilities that manipulate/use ATR’s (outside of the emulator).  I also found a 360K format I was using that was also incompatible outside the emulator.  Note, they do work fine in the emulator.

In this post, I’m describing the required format specifications to achieve a fully compatible ATR image that is sized at 720K, as well as 360K, 180K, and 90K.

I’ll be referencing the Atari800MacX create new disk image utility, accessible from the Media menu:

90K Configuration

When creating the image in Atari800MacX, you can use the standard single density option, or choose Custom and set the sector count to 720 and sector size to 128 bytes.

Afterward using SpartaDOS X Format utility, select the drive number, set the density to “SINGLE”, tracks to 40 SS (single sided).  This should show a sector count of 720 and 128 BPS (bytes per sector), with a  total of 92,160 bytes.  Then use W rite Directory to “format” the disk.

Now you can use DIR to see the contents, which should be empty, and see the number of free sectors which will be slightly less than the 720.  Also use CHKDSK to verify the sector size and total capacity:

180K Configuration

When creating the image in Atari800MacX, you can use the standard double density option, or choose Custom and set the sector count to 720 and sector size to 256 bytes.

Afterward using SpartaDOS X Format utility, select the drive number, set the density to “DOUBLE”, tracks to 40 SS (single sided).  This should show a sector count of 720 and 256 BPS (bytes per sector), with a  total of 184,320 bytes.  Then use W rite Directory to “format” the disk.

Now you can use DIR to see the contents, which should be empty, and see the number of free sectors which will be slightly less than the 720.  Also use CHKDSK to verify the sector size and total capacity:

360K Configuration

There are two formats you can use for 360K images, and both will work. You need only decide which sector size you want based on needed efficiency.  Larger sectors will consume more space for many small files, but will work out better for a few large files.

Option A (512 byte sectors)

When creating the image in Atari800MacX, you need to choose Custom and set the sector count to 720 and sector size to 512 bytes.

Afterward using SpartaDOS X Format utility, select the drive number, set the density to “DD 512”, tracks to 40 DS (double sided).  This should show a sector count of 720 and 512 BPS (bytes per sector), with a  total of 368,640 bytes.  Then use W rite Directory to “format” the disk.

Now you can use DIR to see the contents, which should be empty, and see the number of free sectors which will be slightly less than the 720.  Also use CHKDSK to verify the sector size and total capacity:

Option B (256 byte sectors)

When creating the image in Atari800MacX, you need to choose Custom and set the sector count to 1440 and sector size to 256 bytes. This is more efficient for many small files.

Afterward using SpartaDOS X Format utility, select the drive number, set the density to “DD 512”, tracks to 40 DS (double sided).  This should show a sector count of 1440 and 256 BPS (bytes per sector), with a  total of 368,640 bytes.  Then use W rite Directory to “format” the disk.

Now you can use DIR to see the contents, which should be empty, and see the number of free sectors which will be slightly less than the 1440.  Also use CHKDSK to verify the sector size and total capacity:

720K Configuration

When creating the image in Atari800MacX, you need to choose Custom and set the sector count to 1440 and sector size to 512 bytes.

Afterward using SpartaDOS X Format utility, select the drive number, set the density to “DD 512”, tracks to 80 DS (double sided).  This should show a sector count of 1440 and 512 BPS (bytes per sector), with a  total of 737,280 bytes.  Then use W rite Directory to “format” the disk.

Now you can use DIR to see the contents, which should be empty, and see the number of free sectors which will be slightly less than the 1440.  Also use CHKDSK to verify the sector size and total capacity:

Thats it!

Share this:

  • Click to email a link to a friend (Opens in new window)
  • Click to share on Facebook (Opens in new window)
  • Click to share on LinkedIn (Opens in new window)
  • Click to share on Pinterest (Opens in new window)
  • Click to share on Pocket (Opens in new window)
  • Click to share on Reddit (Opens in new window)
  • Click to share on Twitter (Opens in new window)

Like this:

Like Loading...

iTunes Missing Feature – Library Statistics – Revised

Posted by Ripdubski on 2020.12.06
Posted in: Programming. Tagged: Apple Music, CSS, HTML, iTunes, Javascript, Statistics. 2 Comments

A while back (2018) I created a utility to display playback statistics from iTunes, now Apple Music which I called iStats (https://unfinishedbitness.info/2018/05/04/itunes-missing-feature-library-statistics).  Apple deprecated the automatic creation of the music library XML file not long ago, and that diminished my enthusiasm and support for my iStats utility.  Recently I discovered you can generate a compatible XML file with Apple Music so I added few things to iStats and cleaned it up a bit.  Though no longer automatic, you can still create it on the fly using menu item File > Library > Export Library…

During the update, the tools and resources I used:

  • Microsoft VSCode. (https://code.visualstudio.com).  This is a new tool for me after having used Atom for many years (https://atom.io), which is a very nice editor in its own right.
  • Javascript – Interprested programming language that runs within web browser (https://en.wikipedia.org/wiki/JavaScript) (https://www.w3schools.com/Js/)
  • HTML5 – HyperText Markup Language (https://www.w3schools.com/html/html5_intro.asp)
  • CSS – Cascading Style Sheets (https://www.w3schools.com/Css/)
  • chart.js – Charting library (http://www.chartjs.org)
  • list.js – List library (http://listjs.com)
  • FileSaver.js – Local file saving library (https://github.com/eligrey/FileSaver.js/)

One of the first updates I made was to clean up the Javascript code and start the process of code re-use.  The main effort to date is making functions to set the chart colors.  This cut down quite a bit of redundant code and there is still more work to do.  I’ll work on more later.

The next updates I wanted to include were:

  • Ability to set number of ranked results in charts.  There was a static 10 defined before, now it will allow the selection from 1 to 10.
  • Ability to export the song library to CSV file.  This will allow import into big data analysis tools like Splunk (https://splunk.com) and Elasticsearch (https://elastic.co).  With these tools one can perform all kinds of analysis and reporting on the library.
  • Ability to metricize song duration and total listening time broken down in various terms.

The main page is pretty much the same, along with new version information:

 

The primary difference on the main page is the addition of two more input elements.  These two elements should be set before selecting the music library XML file in the third element since that one starts the report generation process:

 

The elements are self explanatory.

  • For the results number, choose from 1 to 10.  The default is 10.
  • For the export, simply check or uncheck.  The default is checked.  If checked, a CSV file called AppleMusicLibrary.csv will be created and saved to your browsers default save location.  Some browsers may ask for permission to do this.  The song durations in the CSV file are for the song itself, not the duration of listen time for the song.  Everything else exported in it should be self explanatory.

Before starting, open Apple Music and export the Library file in XML format.  Select File, then Library, then Export Library.  Give it a name and location of your choosing.

To begin report generation after selecting the report options, use the “Choose file” button to open a native file selector.  Locate and select the library XML file you previously exported:

Once selected, the application then processes the file, and updates the Document Object Model (DOM – https://www.w3schools.com/js/js_htmldom.asp) with the graph data.  You won’t see any screen updates.  This is due to how Javascript works within the browser.  Any changes in the DOM are rendered in the browser window when the Javascript completes.

At completion, iStats will try to save a file called “AppleMusicLibrary.csv” to your browsers default save location.  You may have to give it permission.

I’ll refer you to the previous iStats post (https://unfinishedbitness.info/2018/05/04/itunes-missing-feature-library-statistics) for general overview of the statistics generated and section output.  

With that said, there is a new section which will appear before the Results section.  This new section is Total Time Listened.  It displays the total combined duration all songs have been listened to accounting for play counts of each.  It will display one line showing the total days, hours, minutes, seconds, and milliseconds.  Following that will be alternative views for total seconds, minutes, and hours listened as single entities:

 

To use iStats, simply extract the linked zip file.  To make things easy, a copy of chart.js, list.js, and filesaver.js are included (distributed via MIT License), but you should download them yourself.  For your own sanity, review the Javascript source in the “iStats.html” file so you can see there is no funny business going on.  It reads the XML file, updates the html page DOM, and optionally saves the csv file, thats it.

iStats is released under the Simplified BSD License.  Feel free to use, modify and improve.

Link to the iStats zip file containing everything you need (hosted at Google Drive): iStats-Distro.zip

Share this:

  • Click to email a link to a friend (Opens in new window)
  • Click to share on Facebook (Opens in new window)
  • Click to share on LinkedIn (Opens in new window)
  • Click to share on Pinterest (Opens in new window)
  • Click to share on Pocket (Opens in new window)
  • Click to share on Reddit (Opens in new window)
  • Click to share on Twitter (Opens in new window)

Like this:

Like Loading...

Posts navigation

← Older Entries
  • Follow Unfinished Bitness on WordPress.com
  • Enter your email address to follow this blog and receive notifications of new posts by email.

    Join 36 other followers

  • Recent Posts

    • Action! Graphics 0 Text Highlight
    • Modern MacOS App for Retro BBS’ing
    • Splunking Apple Music Library
    • Action! String Padding
    • Action! String Trimming
  • Categories

    • Apple
    • Atari
    • Audio
    • BBS
    • Bookmarks
    • DD-WRT
    • General
    • Google
    • iOS
    • MSDOS
    • NAS
    • Notes
    • OSX
    • Programming
    • Router
    • Security
  • Tags

    1Password 8 bit 12v 110v 6502 Action! Alpine Assembly Atari 400 Atari 800 Atari XE Atari XL BASIC Bookmarks boombox BootScript Car Audio CDA7893 CIO CX430 DD-WRT Delicious DHCP DiskStation DNSMasq EverNote Fitbit fitness ghetto-blaster Google GTO605C HTTP Server iCloud instruction IOCB iOS iTunes JBL Local DNS Mac OSX Mac OS X Migrate One online Opcode OSX Perl SimpleNote Synology Windows
  • Archives

    • June 2022
    • December 2021
    • February 2021
    • December 2020
    • September 2020
    • October 2019
    • May 2018
    • April 2018
    • June 2017
    • April 2017
    • February 2017
    • December 2016
    • September 2016
    • August 2016
    • July 2016
    • June 2016
    • May 2016
    • April 2016
    • September 2015
    • August 2015
    • July 2015
    • May 2015
    • April 2015
    • March 2015
    • February 2015
    • January 2015
    • December 2014
    • November 2014
    • October 2014
    • September 2014
    • August 2014
    • July 2014
    • June 2014
    • May 2014
    • April 2014
    • March 2014
    • February 2014
    • December 2013
    • October 2013
    • June 2013
    • May 2013
    • March 2013
    • February 2013
    • January 2013
  • Meta

    • Register
    • Log in
    • Entries feed
    • Comments feed
    • WordPress.com
Blog at WordPress.com.
Unfinished Bitness
Blog at WordPress.com.
  • Follow Following
    • Unfinished Bitness
    • Join 36 other followers
    • Already have a WordPress.com account? Log in now.
    • Unfinished Bitness
    • Customize
    • Follow Following
    • Sign up
    • Log in
    • Report this content
    • View site in Reader
    • Manage subscriptions
    • Collapse this bar
 

Loading Comments...
 

    %d bloggers like this: