Skip to content

hackerb9/crcbas

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

22 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

crcbas

A fast, small, and general purpose CRC-16/xmodem checksum algorithm that can be called from BASIC on the TRS-80 Model 100 computer and the like.

QUICK USAGE:

Download crcbas.do to your Model-T and run it. Usage is meant to be simple and self-explanatory.

Overview

  • crcbas.do (1 KB) is a BASIC program which loads gencrc (see below) and demonstrates how to execute it. It relocates the code into a string and does not interfere with any .co program loaded in ALTLCD or high memory.

  • gencrc.co (55 Bytes) is gencrc.asm assembled to run at memory location 61024. Usage:

    CLEAR 256,61024
    LOADM "gencrc.co"
    i%[0] = 12345                     REM buffer start address
    i%[1] = 256                       REM buffer length
    i%[2] = 0                         REM (initial checksum / result) 
    CALL 61024, 0, VARPTR(i%[0])
    PRINT "Result:" i%[2]

    A large file can be processed in blocks by simply leaving the result in i%[2] instead of resetting it to zero. The final checksum will be the same as if it had been processed as a single piece.

  • gencrc.asm is the source code for gencrc.co. It is merely a BASIC interface wrapped around crc16-pushpop.asm, an implementation of CRC-16 in 34 bytes of 8080 machine language. The push-pop algorithm is relatively fast, requiring an eighth of a second per kilobyte. (See crc16-8080 for faster, larger alternatives.)


Discussion & Digressions

About CRC-16

The CRC-16/Xmodem algorithm is a Cyclic Redundancy Checksum from the 8-bit era of computing. There are actually many CRC-16 variations, and this one happens to be the flavor used by the Xmodem protocol.

If the calculated CRC-16 matches the published value for a file, then there is 99.9985% certainty that the file was received without error. CRC-16 only checks for accidental changes, unlike modern message digests, such as SHA-3, which are also secure against malicious adversaries. Please see crc16-8080 for a better understanding of the algorithm.

Peculiarities of crcbas.do

The crcbas.do version loads gencrc to a BASIC string buffer, which has two benefits:

  • Coexists with any binary blobs you may have loaded into high memory (or ALTLCD).
  • All code is kept in one file for easy distribution and use.

But that comes at a cost:

  • crcbas.do adds about 400 bytes to your program, which seems excessive when gencrc.co is a 55 byte file. However, see the section on gencrc below for why crcbas.do may still come out ahead.

Note that the non-ASCII characters in crcbas.do are treated as text by the Kyotronic sisters (the M100 et al.). Crcbas.do can be loaded over the serial port (RUN "COM:88N1") as if it was a normal BASIC listing in ASCII.

The M/L string is encoded in a custom variant of bang-code that hijacks the ! (bang) character to encode addresses as relative offsets (±32) in the following byte. [Offset byte = Target - Current + 80 (decimal), where Current is the address of the relevant opcode.]

Peculiarities of gencrc.co

Gencrc is meant to be used from a BASIC program, so if one decides to use gencrc.co directly, instead of from a loader like crcbas.do, both the BASIC program and gencrc.co will need to be distributed to end users. The program should start with CLEAR 256, 61024 and LOADM "gencrc.co".

Because the value of MAXRAM (highest usable RAM address, plus one) varies on different models, the gencrc.asm file is "ORG'd" to run at memory location 61024. On a Tandy 200, where MAXRAM is 61104, an extra 25 bytes are reserved and unused. However, on a Model 100 or Tandy 102, where MAXRAM is 62960, 1881 bytes are wasted — a consequential amount on 8K machines. For this reason, the fact that gencrc.co is only 55 bytes is not relevant when comparing its memory footprint against the 400 bytes from crcbas.do's BASIC loader.

On the use of an array of signed integers

The choice was made to use an array of integers to pass the three input arguments instead of the original design of multiple calls to different addresses. This greatly simplified usage and saved many bytes. There are two costs to this choice:

  1. Any program that CALLs gencrc must use VARPTR to get the address of the array. That makes it trickier to use on the NEC PC-8201 and PC-8300 which do not have a built-in VARPTR function. (See below.)

  2. BASIC integers (signified by %) are signed, and it is a fatal error to assign a value over 32767. [Sigh.]

    i%=32768
    ?OV Error
    Ok

    To refer to memory addresses above 32767, one must subtract 65536. For instance,

    READ P$
    V=VARPTR(P$): P=PEEK(V+1)+256*PEEK(V+2)
    IF P>=32768 THEN P=P-65536
    i%[0] = P
    i%[1] = LEN(P$)
    i%[2] = 0
    CALL S, 0, P

NEC PC-8201 and PC-8300 support

Gencrc.co works on a NEC, but the method of calling it from BASIC differs in two ways: VARPTR and HL.

  1. As noted above, N82 BASIC lacks VARPTR.
    1. One solution is to use a shim that implements VARPTR, such as this one from Gary Weber:

      39999 'VARPTR by Gary Weber for NEC 8201/8300
      40000 A=64448
      40010 POKEA,205:POKEA+1,175:POKEA+2,73:POKEA+3,235
      40020 POKEA+4,58:POKEA+5,139:POKEA+6,250:POKEA+8,201
      40030 IFVY$=""THENPRINT"VY$ not defined!":STOP
      40035 A=64457 ' HL register for EXEC = 201/251
      40040 FORH=1TOLEN(VY$):POKEA,ASC(MID\$(VY$,H,1)):A=A+1:NEXT:POKEA,0:POKE64464,0
      40050 POKE63912,201:POKE63913,251:EXEC64448
      40060 L=PEEK(63912):H=PEEK(63913):TY=PEEK(63911)
      40070 RETURN

      However, that's a lot of work to do a job — finding the address of an array of BASIC integers — that we can skip.

    2. A better alternative would be to POKE to reserved, unused memory.

      For example, the addresses from 61096 to 61101 are available. gencrc will use those addresses as input, if we set the HL register to 61096 before EXEC:

      Variable Low High
      Start address 61096 61097
      Length 61098 61099
      Init/Result 61100 61101
  2. N82's EXEC requires HL to be set via POKE:
    Register Location
    A 63911
    L 63912
    H 63913

    For example, POKE 63912, 168: POKE 63913, 238 configures HL to contain the number 61096.

    A theoretical advantage of this method is that one can also PEEK those locations to see results, but it is moot for this program as we return the results by writing directly into memory.

Here is an example that performs a CRC check of the NEC PC-8201's 32K ROM:

CLEAR 256,61024
BLOAD "gencrc"
POKE 63912, 168: POKE 63913, 238		REM HL = 61096
POKE 61096, 0: POKE 61097, 0			REM Buffer address = 0000
POKE 61098, 0: POKE 61099, 128			REM Buffer length = 32768
POKE 61100, 0: POKE 61101, 0			REM Initial sum = 0
EXEC 61024
?PEEK(61100)+256*PEEK(61101)			REM CRC-16 result

TODO: Create a NEC version of crcbas.do.

About

Fast general purpose CRC-16 calculation from BASIC on a TRS-80 Model 100 & Kin

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors