3 minute read

So, TAPE2DISK utility required; simple spec:

  • Can be left to run automatically, lifting data from a tape and storing it on disc (well, a GoTEK!)
  • Won’t need to support files larger than those that will fit in memory
  • Want to reproduce the filename, load address, execution address (and, naturally, the length)

Just needed a reminder of *LOAD and *SAVE, so again.. a web search. Good grief - look what turns up - The BBC Microcomputer Advanced User Guide in PDF form (!) - I always had to borrow one, and now it has been scanned in and OCR’d. Thanks again to StarDot for hosting the archive of bbcdocs.com. Its amazing that so much info is now available! In those pre-internet days, information wsa hard to get hold of - makes me wonder what we could have done with this availability…

A quick flick through the manual and reviewed *LOAD (can just run *LOAD "" 2000 to grab the next file on cassette and store at &2000), and then use *SAVE to write it back out (crucially with a replaced “load” address to match the original). Couldn’t recall how to build a “*” command from a string in BBC BASIC - and tripped over OSCLI() - that’ll be it!

However. How do I get the load & execution address automatically? *OPT 1,2 would let me see it via *CAT etc. - but I need a BASIC programme to see it. There’s a challenge…

More searching. Mind = blown; a full BBC memory map was published by 8-Bit Software Magazine, hosted on nerdoftheherd - namely, Part 1 and Part 2. Digging around, aided by the “Tape to Disc Transfer” articles by Albert Schofield, I was ready. I now know that:

  • 03B2-03D0 Block header of most recent block read

  • B2-BD Filename terminated by zero
    • I can grab the filename, noting its zero-terminated, so:
      filenameChar = &03B2
      filename$ = ""
      REPEAT
      filename$ = filename$ + CHR$(?filenameChar)
      filenameChar = filenameChar + 1
      UNTIL ?filenameChar = 0
      
  • BE-C1 Load address
    • Save that off to a variable:
      loadAddress = !&03BE
      
  • C2-C5 Execution address
    • Again, save that off to a variable:
      execAddress = !&03C2
      
  • C6-C7 Block number
    • 2 bytes this time, not 4, so:
      blockNumber = ?&03C6 + &0100 * ?&03C7
      
  • C8-C9 Length of block
    • 2 bytes again:
      blockLength = ?&03C8 + &0100 * ?&03C9
      
  • File length can be taken to be:
    • 256 * (number of blocks - 1) + length of last block, or:
      length = blockLength + &0100 * blockNumber
      

    We’re in! A quick test to check that I’d built the *SAVE command correctly from the variables, by checking the new ADFS file against the TAPE original, bingo! I then had to dig around to remind myself of the commands I needed to convert integers to a string in hex format (“STR$~(variable)” - there’s a “tilda” that’s not mapped in the MODE 7 font).Full BASIC script is as follows - beware tildas not showing - also stored as a raw text file if you want to *SPOOL it.

    100 REM T2D - a simple Tape to Disc copier
    110 
    120 REM v1.0 - 2020-01-20 - Ian Grimstead
    130 
    140 REM Assumes ADFS, doesn't do anything clever to
    150 REM map "." to different characters or create
    160 REM directories
    170 
    180  REPEAT
    190  
    200  PRINT
    210  PRINT "Loading from TAPE..."
    220  PRINT
    230  *TAPE
    240  startAddress = LOMEM + &0400
    250  REM Show load, exec details...
    260  *OPT 1,2
    270  loadCommand$ = "*LOAD """" " + STR$~(startAddress)
    280  REM  PRINT loadCommand$
    290  OSCLI(loadCommand$)
    300  
    310  filenameChar = &03B2
    320  filename$ = ""
    330  REPEAT
    340  filename$ = filename$ + CHR$(?filenameChar)
    350  filenameChar = filenameChar + 1
    360  UNTIL ?filenameChar = 0
    370  
    380  loadAddress = !&03BE
    390  execAddress = !&03C2
    400  blockNumber = ?&03C6 + &0100 * ?&03C7
    410  blockLength = ?&03C8 + &0100 * ?&03C9
    420  length = blockLength + &0100 * blockNumber
    430  
    440  saveCommand$ = "*SAVE """ + filename$ + """ " + STR$~(startAddress) + " +" + STR$~(length) + " " + STR$~(execAddress) + " " + STR$~(loadAddress)
    450  PRINT
    460  PRINT "Saving to ADFS..."
    470  *ADFS
    480  REM  PRINT saveCommand$
    490  OSCLI(saveCommand$)
    500  PRINT "...saved"
    510  
    520  UNTIL FALSE
    

    Thought for the day: a C60 (that’s a 60 minute cassette) pulled in 160kB of data. Ouch. Took a handful of seconds to save to ADFS - I now remember the joy of floppy discs!