Sunday, February 14, 2021

A method to relocate a BASIC program

On an Apple II with ROM BASIC, a BASIC program can run anywhere there is space in memory with the caveat that one step must be performed or the NEW command might fail. The first byte of the fresh location where BASIC will run must be set to 0. 

BASIC initially locates programs at $800. The decimal conversion of $800 is 2048.  To demonstrate the problem caused by the first byte, enter a fresh BASIC session. Issue the following commands:

] NEW
] LIST

] POKE 2048,255
] NEW
?SYNTAX ERROR 

] POKE 2048,0
] NEW

* no syntax error is encountered

] REM $0C00 IS 3072 IN DECIMAL
] POKE 3072,255
] REM $0C IS THE HIGH BYTE AND IS 12 IN DECIMAL
] POKE 104,12
] NEW
? SYNTAX ERROR

] POKE 3072,0
] NEW

* no syntax error is encountered


This is fantastic. A program can be located above the TEXT PAGE 2 ($0800-$0BFF) and use it for LORES graphics.  But, these steps must be performed each new session.

] REM START BASIC ABOVE GR PAGE 2 AT $0C00
] POKE 104,12
] POKE 3072,0

Could this be automated if loading from DOS or ProDOS? 
If yes, could it be automated per each program?

The short answer is yes & yes. The answer is provided in part by left over, or stale, data in memory.  I think of that state of data like dinosaur bones. That data is a sign of what happened in the past and may only be interesting to a few people. 

Retrieving that data in a BASIC program can be performed by using the PEEK command provided some awareness to where the data resides. If the data happens to be ASCII text, that data can be exposed to BASIC by using the POKE command to alter an already defined string.  I will demonstrate this later.

For my purpose, I will use the input buffer of ProDOS stored at $BCBD and the input buffer length at $BCBC.

Process Background
BASIC uses the zero page when operations are performed on variables.  There are dinosaur bones of BASIC variables found in the zero page. In particular, a region identified as scratch registers by Jon Relay's* very useful Apple II references.

Via trial and inspection, I discovered that scratch registers $85/$86 and $8C/$8D are affected when a new string is assigned the value of an old string.  In my example, N$ is the new string and O$ is the old string.

This assignment leaves dinosaur bones in the zero page, specifically the most recently used addresses of the strings.
N$ = O$

In this assignment, $85/86 is the location in memory of the old string and $8C/$8D is the location of the new string.

$85 = low address of old string
$86 = high address of old string

$8C = low address of new string
$8D = high address of new string

In the references* mentioned above, a keen reader will notice that the last used variable address is found in memory at $83/$84.  In the assignment operation N$ = "ascii text", $83/$84 is very much equal to $85/$86, and is the primary last used variable address and not one of the scratch registers.  
 
$83 = 131 in decimal
$84 = 132 in decimal

At first glance, this might appear to provide direct access to the string variable:
A = PEEK(131)+PEEK(132)*256

However, capturing the address of the numerical variable is not directly possible with a numerical variable. The address of the numerical variable receiving the value is assigned to $83/$84 and $85/$86 regardless of any operations on the right side of the assignment.  The value stored in the new variable is the address of itself, and not the anticipated address of the string.

Addresses $83 ... $86 will not keep value when capturing the last used variable address as a numerical variable. 

Lucky for us, the knowledge of how $8C/$8D store the last used variable addresses can help in this situation. 

Assigning a new string with the value of the old string, and even self assignment like O$ = O$ will suffice and keep a copy of the address of O$ in $8C/$8D. 

If the next operation is as follows, the address to the string is obtained:
A = PEEK(140)+PEEK(141)*256

Now for a word about how the string is stored.  The string payload, as in the ASCII text, is not located inline with the variable. The byte stored at A is the string length.  A+1 and A+2 are a pointer to the absolute locations of the string.  A+1 is the low byte and A+2 is the high byte of the string payload pointer.

We have the location of the string length and the pointer. With both these pieces of information, we can resize and repoint the string to the name of the program. 

This will be useful if we RUN THE.PROGRAM to get it loaded at our desired BASIC location.  If at first your LOAD the program, a RUN will not succeed.  

If the intent is to LOAD the program, this method is not needed as a the POKE statements on lines 10 and 20 can be issued before the LOAD.  This code that follows takes care of loading the BASIC program to the desired location.   

10 IF PEEK(104) <> 8 GOTO 50

20 POKE 12 * 256,0: POKE 104,12

30 A$ = “/”: A$ = A$: A = PEEK(140)+PEEK(141)*256: POKE A,PEEK(48316): POKE A+1,189:POKE A+2,188

40 NA$ = A$: PRINT CHR$(4);”LOAD”;NA$


Line 10 checks if the BASIC program location is at the default.  If it has already been altered, skip to program execution.
Line 20 clears the problematic first byte of the relocation address and then sets the high byte of the BASIC program location to 12 ($C00). 
Line 30 defines A$, performs the assignment that leaves dinosaur bones in $8C/$8D, stores the address from $8C/$8D in the variable A, pokes the value of 48316 ($BCBC) to the string, and closes by setting the high and low bytes of the ProDOS keyboard input buffer to A+1 and A+2.
Line 40 performs an assignment to copy the string, and invokes a ProDOS BASIC LOAD of the file.

There and done.

More on Variable Zero Page registers
In my experience, the last used variable address pointer in the zero page is only valid immediately after a variable has received an assignment.  Further instructions may clobber it.  Take this derivative of the code above which is not relocated:

30 A$ = “/”: A$ = A$: A = PEEK(140)+PEEK(141)*256: POKE A,PEEK(48316): POKE A+1,189:POKE A+2,188

40 NA$ = A$

50 PRINT PEEK(131)+PEEK(132)*256

60 GOTO 60


The output is 2163 from line 50, which in fact points to the original A$ having the updated paload pointer to $BCBD, but as of executing the GOTO in line 60, that value of decimal addresses 131/132 ($83/$84) are now $824, somewhere in the middle of the BASIC code.  The scratch registers at $85/$86 point to $881 and scratch registers $8C/$8D point to $009D.  No time is spent on $8C/$8D since the pointer destination fails to correspond to any string payload.  $881, however, points to the string region $95F7.  Inspection of the Start of string storage $6F/$70 yields $95F7, confirming a viable region in which BASIC is allowed to provision string payloads. The payload for the $NA assigned in line 40 and located in memory at $95F7 has the ASCII string RUN (CTRL-M) and substantiates that the variable assignment at line 40 successfully copied the data from the $BCBD text input buffer.


This hopefully stresses the importance of fetching the payload pointer immediately after performing an assignment that populates $8C/$8D.


Thanks for reading.






Friday, December 11, 2020

Apple cider vinegar and gnats not quite as good as...

In 2018 when fall arrived, I brought my plans back indoors.  With them came fungus gnats. I could not get rid of them even with an apple cider vinegar solution.  I left it out for over a week and not a single catch.

In 2020 when fall arrived, I brought some strawberry clippings indoors.  They had a few weeks in small pots to establish roots. But like 2018, when they came in, so did fungus gnats. 

I tried H2O2 rinse of the soil; that didn't work.  I tried a soapy solution along the bottom of the planters and wetting the topsoil surfaces.  That also did not work.  I tried a dusting of diatomaceous earth and saw no reduction in gnats.

Lastly I tried my own sugary water solution to attempt to catch the gnats and much to surprise, this works quite well.  I think I will try this along with another H2O2 rinse.

The mixture consists of 4 cups of brown sugar mixed with 1 gallon of water


Wednesday, October 21, 2020

TUYA camera offline media files, offline!

Following a vehicle theft from in front my house, I became keenly interested in DIY camera surveillance.  One of the types I tried is a TUYA Smart Battery Wifi Camera.  Nothing but a sticker on the box genuinely indicates it is marketed by TUYA.  Most everything is generic black and white documentation.

The camera RTSP's to a TUYA cloud, and there is nothing about the wireless feature that I like. It only connects to an open network.  There is no management access to the camera core.

The quality cannot compare to Lorex.  One feature this camera has, which a Lorex system does not, is an SD-CARD slot.

I positioned the camera for an overnight watch of my backyard.  It is motioned activated, and only captured my positioning and a single clip that seemed to be the result of the neighbor's porch light.

The SD-CARD contained a DCIM directory and because the camera is not configured, a date of 1969.  The subdirectories had a simple .info file with the content "V1" and a handful of .media files.

Neither /bin/file, binwalk, nor vlc could make sense of the .media files.  Some lucky search results indicated this was a NHNT interleaved video format.

I took a wild guess that FFMPEG might be able to transcode.  Sure enough, it does

ffmpeg -i 0001media /tmp/video.mpg

And there you have it.

I wrapped the conversions in to a basic shell command.

I=1; find -name *.media | while read X; do ffmpeg -i ${X} /tmp/$(basename $X)_$I.mpg; I=$((I+1)); done

Thursday, October 1, 2020

Magic, as in Crosswords not Unicorns




* Recently => September 2020

Recently*, a famous, mysterious cracker (who I dub a "KraXpert"TM), in the Apple II community posted his findings about additional latent copy protection on an Apple II title called Crossword Magic.  The version he mentioned was 4.0. 

I remembered that while in high school, years 1988 - 1992, my primary school had a software title by the same name.  For reasons I cannot explain, I felt no guilt about having made my own copy of the program and keeping it in my library. 

When this came on topic, I located the disk and booted. It was in fact version 4.0.

This KraXpert had identified that Crossword Magic 4.0 had three recurrences of an E7 bitslip protection, each with a slight variation.  

Back in the day, I discovered my own method to slip around the E7 protection scheme.  The method allowed for easy bit copying in most cases.  I never had any motivation to make these backups normalized 16-sectors.

In fact, it employed some minor changes to a standard 16-sector format, and my knowledge of sectors was limited back then, so the slightly modified backup served my purpose. This particular backup showed evidence of my resync technique characterized by invalid sector checksums.

This KraXpert gave some examples of how an incomplete recreation of the E7 fields fails in interesting ways. One type of failure results in clearing the all memory and stopping at ] cursor prompt.

Using some powerful disk imaging (Applesauce), the KraXpert revealed where the extra zeroes appeared in the E7 stream.  Only the correct pattern would pass muster.  I questioned whether my copy worked in the same circumstances this KraXpert would be using - an emulator.  But first I would test on actual equipment.

TL;DR? Jump to the end of this blog post.

I booted my backup on a real //e, absent a printer interface, and although it began to print, it failed quickly to a prompt.  

I imaged the disk with Applesauce and tried a WOZ in Virtual II. It printed perfectly.  

Although this worked perfectly, it was in WOZ format, which preserves a remarkable amount of bit integrity necessary for sophisticated copy protection to survive inside emulation.  

I anticipated the the KraXpert would be using a tool of their own design that completely normalizes DOS-based RWTS.  I have used this tool to verify disks; it's an amazing piece of software that helps with validation of different title releases by normalizing to a standard 16-sector format.  

The most recent release scans through an original disk of Crossword Magic and finds unformatted tracks $12 through $22.  It uses the disk's own DOS to read sectors, and succeeds on tracks $F through $11.  Upon reaching track $E it gets an error. On my backup copy it failed at track $F due to an error I had made long ago.  I located the WOZ-A-DAY version and started with it, fancying I could always re-establish my E7 sequences.

Unable to use this tool to normalize Crossword Magic, I attempted to track down a tool I used in my youth.  I did not find that old tool, but I discovered that Applewin, in combination with Copy II Plus 8.3, was able to write an unprotected  version of it in a DSK format.  All sectors, with DOS 3.3 PATCHED set from the sector editor, ended up reporting an error 5 with SECTOR COPY, but the contents in most cases were correct.  The exception surfaced when Copy II Plus 8.3 was used to perform a sector copy of either bad sectors (like my WOZ) or empty tracks $12 through $22, where Copy II Plus appeared to fill in already established sector data across all those tracks.  Realistically, those tracks should have all blank sectors (either all zeros or all FF's).  

Now that I had the disk contents unprotected, I could begin testing if my E7 sequence worked universally or if only in special cases. This is where the fun began.

The first hold up began in the boot loader in track $00 sector $00.  The boot sector uses the disk II ROM to pull in sectors, but on its own terms.  Once it is through with track $00, it calls a disk arm routine to advance to track $01.  It continues to use the disk II ROM routine, but it now fails to read any sectors from an unprotected disk's track $01.  This is because the disk II ROM loader expects to only load from track $00 unless the caller has set zero page $41 to $01.  This constitutes the first bit of code patching.

After it gets to track $02, it now has its own early sector header locator in text page 1. The routine in this page specifically requires that the header epilogues end with $FF and the sector loader in page $5A00 end with a sector epilogue of $FF.  Patching as follows in Applewin gets through the boot process

$44D => DE

$457 => AA

$5ACE => DE

The secondary protection is E7 field based.  The main program will fail if the DOS sector at track $02 sector $08 lacks pattern

EE E7 FC EE E7 FC EE EE FC

If that sequence exists in the E7 field, the main menu will allow for selection of various operations; however, as the KraXpert reported in a Apple II workspace, symptoms will appear; such as the inability to play a puzzle and successfully print. I do not know this KraXpert personally, so I do not know if the symptoms are caused only by an invalid E7 sequence or related to other alterations.  For instance, I propose alterations below that may have a latent effect.  

Moving on, there are two more E7 fields on track $11.  If either one of those is absent, the protection check before printing will fail and it will ask again for the program disk.  The KraXpert; however, noted that an incorrect pattern may result in endless printing of garbage. 

Because this disk is no longer in a protected sector structure, another likely protective measure, that allows for successful reading of the original disk's protected tracks, will now fail because it adjusts the epilogues back to the $FF's before the check and restores them to $DE AA / $ DE following the check.

AppleWin provides a convenient means to inject code through the debugger (F7 - debugger).  After lifting the 16-sector protection for tracks $2 through $E, the boot sequence should be disrupted through a breakpoint at $802 by using the following debugger command prior to booting (F2 - reset). 

BP 802

Resume execution with F7.  When AppleWin breaks at $802, supply the following commands in the debugger.

MEB 863 20 E0 08

MEB 8E0 E6 41 4C 82 08

MEB 879 20 E5 08

MEB 8E5 A9 DE 8D 4D 04 8D CE 5A A9 AA 8D 57 04 A9 02 4C 82 08

Resume execution with F7.

When reaching the CROSSWORD MAGIC splash screen, enter the debugger (F7 - debugger) and enter the following command at the debugger.

MEB 9FE7 8C

This final change will cause the code to  $DE AA epilogues instead.

I am repeating the sequence from above - track $2, so it is closer to the latent E7 sequences on track $11, for the sake of easy comparison.

Track $02, DOS sector $08 can work with this E7 field sequence.

EE E7 FC EE E7 FC EE EE FC

Raw sector AB AF can work with this E7 field sequence.

EE E7 FC E7 EE FC EE FC

Raw sector AE AB can work with this E7 field sequence.

EE E7 FC EE E7 EE FC EE FC

All of the above E7 sequences must be inserted at the correct position in the respective E7 field.  A nibble editor can be used to insert the sequence, followed by a sector editor to regenerate a proper checksum.**    

At the start of any E7 field, the first three E7's are left as is; this is the accepted approach used by the KraXpert.  The next three signature bytes are EF F3 FC, followed by the desired E7 sequence from above.

E7 E7 E7 EF F3 FC pp aa tt tt ee rr nn aa bb oo vv ee 

TL;DR From limited testing in AppleWin, MAME, and Virtual II, it behaves identically to a WOZ version. MAME shows an interesting video artifact with both //e and //c after selecting some of the menu options, and this artifact is present with the WOZ.

Another interesting discovery, Crossword Magic detects a //c and prevents changes to the printer interface and slot.  Larry did an incredible job on this title.

// that's all folks. thanks for reading.

** One caveat I found is that if the sector on track $02 is corrected in a nibble editor and written back to a DSK type image in Applewin, sector $00 will be written as a blank sector, which yields a very interesting effect in Crossword Magic.  The effect is that the blank sector is all CPU BRK instructions, for which Crossword Magic has a BRK handler.  It cleanly exits to a ] prompt for showing the title screen.

Sunday, September 20, 2020

Android to IOS and the elusive call logs.

 Wanting to move back to IOS, I found myself unable to succeed in whole with any of the available tools. I read a few reviews of top apps, and made an attempt with X-Transfer (available on both IOS and Android) but the app crashed upon scanning SMS.  So quickly in fact that I could not even provide the developer feedback within the app.

I tried Move to IOS but it wouldn't transfer photos,  I tried without photos and it worked, but it was missing my text messages.  My call log did transfer, but at this point I am unable to reproduce it.

I elected to use another  Android phone as a go-between.  I set the backup to exclude Photos and it quickly completed.  I restored the backup to the go-between and it synchronized both SMS/MMS and the call log.  

I tried Move to IOS, and it completed everything including SMS/MMS, but without ever transferring my call log.  I repeated a transfer with X-Transfer, but it failed to allow me to target my call log - the call log was greyed out.  I tried a different tool, but likewise, it would not import to the IOS 13.5 call log.

I finally decided to connect adb to the go-between and manually fish out the call logs.  

I found them as an SQLite DB under /data/data/com.android.providers.contacts/databases/calllog.db.

The date for each call appears to be epoch-time format but needs to be divided by 1000 to be consumed by the Linux date command.


Sunday, September 13, 2020

An unexpected wipe out (AICP style)

My Pixel completely stopped receiving security updates in October 2019.  One year later it's time to move toward a more secure device.  

Last night I decided to see if I could restore my Android 10 Pixel to another Android 10 instance.  The motivation is to capture a point-in-time snapshot of my texts and calls; data which I've found no way to transfer from Android to IOS (the converse however works awesome thanks to the migration tool Google provides in the Pixel).

I had no data of interest on the HTC m8 selected to receive the restore.  I invoked reset all data through the Android 10 UI and it reboot in to Captain Throwback's SAR TWRP (already installed).  I used the defaults for wiping.  When I rebooted, it dived straight into recovery.  I tried adb sideload downloadedbuild.zip, but still went to recovery next boot.  I tried adb push downloadedbuild.zip /sdcard and installing through the TWRP UI, but same result; recovery.

Finally I remembered adb shell and fumbled around with sgdisk to check out if I had a good partition table on mmcblk0.  I did.  cache and data were mounted automatically, but system did not automatically mount.  I went back in to the mount function of TWRP and mounted system.  Back at the shell, it was now mounted at /system_root.  I repeated the adb push downloadedbuild.zip /sdcard and installed it. Upon reboot it booted in to the splash screen instead of recovery.

You might think I was in the clear at this stage, but the entire time writing this, it stayed on the aicp color changing boot screen.

So in closure, the point of this post is that system_root ha to be mounted in order for the aicp 10 zip to successfully apply.  I suspect my issue is that I need to back level to an aicp and 'apply' this 10 zip over the top. We'll see.

See, I did.  I ended up booting back in to TWRP and after several rounds of trying a sideload over of AICP and Lineage 17, I think I finally found the right combo that results in loading instead of faulting back to invoking TWRP.

if already in an Android 10 build, perform a factory reset

in TWRP choose a wipe and format

push over LINEAGE to /sdcard

mount system

install from /sdcard 

optional, test - but it might not complete boot (pwr+vol down for 10 seconds)

if not in TWRP, boot back in to recovery

invoke side-load

mount system

side-load AICP

optional - test, that it works before installing open_gapps, then boot back in to TWRP

side-load over open_gapps-arm-10.0-micro-20200912.zip

that should do it


phone.android.com continued to crash. Setup appeared to restore from backup correctly.



Saturday, June 6, 2020

Must I repeat myself?

I mentioned in a past post that I snagged an HP ENVY and upgraded it  It is now beyond the HP 90-day factory refurbished warranty and I have pushed myself to use it daily as a way to diagnose the frequency of the hardware issues. 

These intermittent hardware issues have come up over the last two months and could be related to the upgrades.
Freeze or blue screen followed by an inability to boot
Freeze or blue screen followed by an inability to power on
Always passes a quick system test and full memory test
Blue screens either a fault in an ATI driver or a page fault in non paged area
A device disconnected notification from Windows when waking up from suspend.

One issue I suspect is unrelated to a hardware issue is a complete disregard of a fastest setting of the keyboard repeat rate when waking from suspend. A reboot always fixes the issue, but I also found that adjusting the Repeat Delay to full left Long, Apply, then full right Short and Apply fixes the issue without a reboot