******************************************************************************
;
;     ****  *****  ****   *****   ***   *       * *          ****  *   *   ****
;    *      *      *   *    *    *   *  *       * *         *      *   *  *
;    *      *      *   *    *    *   *  *      *****        *       * *   *
;     ***   *****  ****     *    *****  *       * *          ***     *     ***
;        *  *      * *      *    *   *  *      *****            *    *        *
;        *  *      *  *     *    *   *  *       * *    **       *    *        *
;    ****   *****  *   *  *****  *   *  *****   * *    **   ****     *    ****
;
******************************************************************************
*   NOTICE:  This file and listing are proprietary and a trade secret of SD. *
*   It is NOT to be released to anyone other than the Director of Software   *
*   at SD without written permission of the President of Software Dynamics.  *
*   (This policy ensures that detailed knowledge of the mechanism is kept    *
*    to an absolute minimum, maximizing the security of the mechanism).      *
******************************************************************************
*
*        REVISION HISTORY
*
*
*        SERIALNUM11D 5/21/86
*               Make SERIALNUMBER.SYS write registered version of self back
*               into original file and then truncate (do CC:SETFILESIZE).
*               This prevents a DISK FULL error from occurring when registering
*               an SDOS on an (usually) extremely full production system master.
*               Added subroutine to use to help compute checksum over code.
*        SERIALNUM11C.ASM 7/22/84
*               Add special switch for Color Computer, which has a lousy
*                   screen (16 rows by 32 characters) and which requires
*                   locations $103-$112 to be free.
*               Use Computer Systems Distributors as registerer instead
*                   of SD to prevent SD from getting drowned with phone
*                   calls from CoCo crazies.
*        SERIALNUM11B.ASM 8/20/82
*               Fixed "30 minute delay seems to be 0-4 hours" bug
*               Changed to use SYSCALL:GETSERIALNUMBER
*        SERIALNUM11A.ASM 3/19/82
*               Modified to say "only on the single computer with"
*        SERIALNUM10.ASM 11/21/81
*               Fixed spurious ^@ (null) printout after serial number.
*               Modified to write protect self upon registration.
*
         page
*        The file SERIALNUMBER.SYS is a major component of SDOS copy protection
*        This file is required to be on an SDOS boot disk or SDOS will not boot
*        It is an Encrypted object file, and contains several things:
*                A first-time-only conversation with the purchaser of SDOS;
*                The serial number of the computer for which the particular
*                   of SDOS was sold (in the form of an Encrypted file record);
*                The name of the end user (organization);
*                A secret signal to SDOS that allows SDOS to run indefinitely
*
*        The SDOS module, after initiating operation of the system,
*        goes and hunts for SERIALNUMBER.SYS. If this file does not exist,
*        SDOS displays and hangs up with a "No SERIALNUMBER.SYS file" error.
*        If the file does exist, it is CHAINed to, causing implicit decryption.
*        The SDOS decrypting loader will refuse to load SERIALNUMBER.SYS
*        if the serial number encoded into it does not match that of the ROM
*        included in the system hardware; this causes a
*        "Wrong Serial Number" message to be printed, and
*        operation of SDOS is aborted.  Otherwise, the module is loaded
*        and executed.  If this is not the first time SERIALNUMBER.SYS has
*        been loaded, then SERIALNUMBER.SYS first prints the ROM Serial number,
*        and the name of the end user; further operation of SDOS is normal.
         page
*        The name of the end user is supplied by the end user when the copy
*        is first run by him; i.e., if the end user name is blank.
*        SERIALNUMBER.SYS asks the name and then waits for the operator
*        to enter a corresponding code number that he must obtain from SD.
*        This code number is generated by SD from the serial number of the
*        computer and the string entered by the user (this may be obtained
*        from SD well in advance of system installation to minimize delays).
*        An invalid response is so indicated, and the end user name is
*        NOT updated. A correct response
*        causes SERIALNUMBER.SYS to change the end-user name to the supplied
*        string.
*        Once set, the SERIALNUMBER.SYS file can never be changed
*        again; however, the customer may save or obtain a virgin
*        SERIALNUMBER.SYS if the customer really needs it.
*        The 'empty' response is taken as a signal that the user does not
*        wish to set the name yet (this may be a demo copy, or the user
*        has not yet obtained the corresponding code number from SD);
*        in this case, after a 30 minute delay, SDOS will operate normally.
*        Note that nobody can assign an end-user name twice without SD
*        learning about it, and that the boot-time delay is so inconvenient
*        that no user will suffer it long.
*
*        The standard SD serial number is supplied by the distributor
*        using a program supplied by SD.  This Serializing program
*        accepts a SERIALNUMBER.SYS file that has the distributor's
*        name encoded into it (by SD) for traceability.
*        No distributor can insert a phony name.
*        The serializing program will refuse to run monthly
*        unless fed a number from SD based upon the number of Serial numbers
*        issued the previous month.
*        The current date is entered into a SerialNumber.SYS file so
*        that SD may determine when the Serialnumber file was created
*        and charge interest accordingly.
         page
*        What protection do we have against an SDOS thief?
*        0) The theif could xerox the ROM containing the serial number.
*               Answer: Only with ROM blowing hardware. This is even more
*               inconvenient if the ROM is soldered into the CPU board.
*               And if we find out, we NEVER sell him another piece of software
*        1) The thief could build a SERIALNUMBER.SYS file from scratch,
*               or modify an existing one by patching.
*               Answer: SDOS will only load it if it is encrypted, and
*                       we don't publish the key (a secret function of the
*                       ROM serial number) so he can't encrypt it right.
*        1a) The theif could discover the secret key generating function in
*            SDOS, and then do 1).
*               Answer: Well, we hide and obfuscate the key generator
*               as well as we can.
*        2) The thief could determine the key by stopping the system at
*               the right instant with:
*               A) A non-maskable or a restart interrupt
*                   Answer: how does he choose the right moment without
*                           modifying the hardware?
*                           Also, when loading an encrypted file, an NMI
*                           is ignored, and restart zeroes memory.
*               B) A logic state analyzer
*                   Answer: This one can't be stopped-- don't keep
*                           key in contiguous locations, and make the
*                           code obscure.
*               C) A breakpoint or modifying SDOS to save the key someplace
*                   Answer: SDOS checks itself to make sure it is not changed.
*        3) The thief could patch SDOS to ignore SERIALNUMBER.SYS or to accept
*               an incorrect serial number.
*               Answer: SDOS must check itself to verify this has not
*                        happened. A first-level, simple checksum is
*                        used to verify that the memory has not gone
*                        accidentally sour. (Note that the "checksum" routine
*                        is not an "add all the bytes" because simple
*                        patches could then be made by subtracting the value
*                        of the patch from a byte in a part of SDOS that is
*                        not needed by the thief. Several secondary checks
*                        (to cross-check each other) should be implemented;
*                        these checks, if they fail, should simply cause
*                        system operation to be unreliable. Note that the
*                        checksums so generated must be constants built at
*                        SDOS assembly time, not SYSGEN or BOOT time.
*        4) The thief can stop the system while it is running SERIALNUMBER.SYS
*               and patch it so it doesn't check the serial number.
*               Answer: SERIALNUMBER.SYS checks itself after verification
*                        to ensure it is not modified. Like all other
*                        programs that checksum themselves to protect
*                        themselves, a trivial check should exist and a
*                        nasty (much later surprise) check should exist.
*
*whats to prevent some fink from patching an existing I/O package or
*sysgenning an I/O package that gives control to his own private debugger
*on a special signal???  NOTHING, at the moment. Don't tell...
*
*  What about a system using page map hardware... what gaurantee is there
*  that the page map will be set up to point to all the encrypted programs
*  at any instant?
          page
*         Boot ROM Structure:
*              SD requires its low-volume vendors to have a certain code
*         sequence in the Boot ROMs. This code is explained below (not
*         currently having enough leverage to make high volume vendors
*         put this information in the ROMs, specially modified versions of
*         SDOS are required to run in such systems; details of such
*         modifications are manufacturer dependent and are not included here).
*         To prevent the system from being stopped while an encrypted
*         program is being loaded or is running, the Restart and NMI
*         vectors must check an "Encrypted program is running" flag; if it
*         is, then memory must be zeroed (this modification MUST be in the
*         ROMs, and the ROMs should be soldered into the processor board
*         for maximal protection). Likewise, this flag must also prevent
*         the debugger from obtaining control via ^D (enforced by VT
*         driver). SDOS locates this flag by following the Restart vector
*         to a Jump instruction, which must point to a fixed instruction
*         sequence (see below).  The extended address specified in the LDAA
*         instruction is defined as the location of the "Encrypted program
*         is running" flag; if this code sequence does not exist in the
*         ROM, SDOS will refuse to load any encrypted file (especially
*         SERIALNUMBER.SYS), thus enforcing the security of the encrypted
*         programs (this is checked in the SDOS initializing code).
          page
*         The Restart sequence must be as follows:
*               $FFFE --> JMP $xxxx
*                         [8 bytes of Cpu Serial Number]
*                         ...
*               $xxxx --> LDS  #anyvalue           Set up stack pointer for JSR
*                         LDAA RunningEncrypted    [Extended mode reference]
*                         BEQ  Dontzeromemory      b/ Not running Encrypted
*                         LDAA #numberofbanks-1    = largest number of banks
*               EraseBank JSR  SELECTBANK          Turn on bank given by (A)
*               EraseMem  LDX  #$BFFF              Yes, zero out user memory
*               EraseLoop CLR  0,x                 Make it go away.
*                         DEX                      Get all of it...
*                         BNE  Eraseloop           B/ Not done yet.
*                         DECA                     Preserved by SELECTBANK
*                         BPL  EraseBank           Assert: Numberofbanks<128
*               Dontzeromemory ; Where to go if not running encrypted
*                         ...
*                         CLR  RunningEncrypted    Mark "Not running encrypted"
*                         ...
*          Similarly, the NMI sequence must be as follows:
*               $FFFC --> LDAA RunningEncrypted    [Extended mode reference]
*                         BNE  AnyRTI              B/ Running encrypted, ignore
*                         ...                      No, do as we please...
*               AnyRTI    RTI                      How to get back from NMI
*
*          SDOS verifies that the above code exists in ROM; it
*          also verifies that SELECTBANK does the proper thing by USING it.
*          For instance, single-user SDOS simply selects bank zero before
*          running; if the routine does not work, single-user SDOS will die,
*          and if it does work, single user SDOS will run in whatever space
*          it selects.  Likewise, SDOS/MU does the same thing.  Note that
*          SDOS/MU must interpret the NumberofBanks constant only as an upper
*          bound, because that many memory banks may not actually be installed.
*          SD should insist that systems
*          with multiple spaces have the ROMs soldered in to make defeating
*          the zeroing logic very difficult.
           page
*           SDOS sets the Running Encrypted flag whenever it encounters a
*           CHAIN to an encrypted module (a LOAD request aimed at an
*           encrypted module is not honored unless the module to be loaded
*           is encrypted with the identical key as the module invoking
*           the LOAD). Before loading the CHAINed-to module, SDOS
*           first fills the user space with random numbers that depend on
*           the file being loaded to confuse anyone who might happen to
*           look into the memory of the machine. After loading the
*           encrypted file, SDOS will trash the key used to decrypt it. The
*           "Running Encrypted" flag is cleared only after an EXIT or
*           a CHAIN Syscall has erased the contents of the user memory,
*           thereby ensuring that there is no way for a user to examine
*           memory once an encrypted program has loaded (decrypted).
*           If a running encrypted program
*           issues a LOAD/CHAIN, the userspace is trashed to prevent phony
*           CHAINed-to modules from taking a snapshot of the user memory
*           and thereby side-stepping the encryption on the CHAINer.
*           However, if the CHAINed-to program has the identical decryption
*           key, the userspace is NOT trashed; this allows an application
*           suite to CHAIN among its components, passing COMMONed variables
*           (The encrypter program will allow the person performing the
*           encryption to specify a secret word to be used as the input
*           to a function that generates the random part of the :05 record
*           so that all modules in the same suite can end up with identical
*           decryption keys).  Note that the decryption key itself is not
*           kept around; only a unique function of it (like an
*           encrypted copy of the decryption key).
          page
*      When SDOS is booted, it sets the RunningEncrypted flag several times
*      to prevent a theif from loading SDOS into memory, clearing the flag,
*      and turning it loose.  Is this necessary???
*
*      SerialNumber.Sys signals SDOS that all is well, and it is ok
*      to run by doing a CHAIN with a funny Parameter field in the syscall block;
*      the parameter enables SDOS to run for long periods without going <BOOM>;
*      otherwise, SDOS dies a horrible death on a subsequent "CHAIN"
*
*       NOTE: if the thief knows how the code works, then he can merely
*       stop the system while it running the serial number module, patch
*       the appropriate places, and continue.  So we might as well assume the
*       thief does NOT know how the code works, and ensure that this is the
*       case.
*
*
*        In Chain, in SDOS, the following funny code exists:
*        ....
*        <do idiot checksum on self>
*        Ldx   addressofexitblock
*        ldx   scblk:params,x    Fetch SDOS enabling key
*        jsr   Safegaurd
*        ...continue here...
         page
*        In the data area, the following code has been Assembled (for 6800):
*        nop            These 3 nops force disassemblers in false sync
*        nop
*        nop
*        fcb    $B7     Stb extended opcode, to keep disassembler in false sync
*   Safegaurd ; entry point to confusing piece of code
*        fcb    $FF     Stx extended
*        fdb    *       store SDOS enabling key here
*   Safegaurd1
*        rola           Red herring instruction; ignore
*        Ldx    #$3B3B  "RTI, RTI" opcodes
*        ldaa   #$31    "INS" opcode
*        stx    Safegaurd1  plant RTI, RTI after SDOS enabling key
*        staa   Safegaurd  plant INS before SDOS enabling key
*        *              Now ruin user space so Defaultprogram, Initialize.sys
*        *              cannot see user space containing SERIALNUMBER.SYS code
*       jmp    Zapuserspace  (which is a subroutine)
*
*        This code, when executed with "INS,RTS" in (X), self-modifies
*        to produce the following code:
*   Safegaurd ; entry point to confusing piece of code
*        des
*        ins
*        rts
*        rti
*        rti
*        rti
*        rti
*
*        It is obvious that any "key" fetched on the 1st Chain, if not
*        the sequence "INS,RTS" will cause the system to blow sky-high
*        on the second call to Chain.  Thus we are protected in an obscure
*        fashion.
         page
*       To prevent SDOS from being modified (allowing a file to be decrypted
*       and stolen), SDOS has two checks in it: a relatively simple check that
*       is used to defend against bad memory, and announces detection of
*       modifications to SDOS, and a much more subtle test (that takes longer
*       to run) that simply makes SDOS unreliable.  The simple test will
*       always beat the subtle test to an announcement that SDOS is sick,
*       thereby preventing SDOS from becoming unreliable without due notice,
*       unless the simple check is defeated (by a would-be pirate, who will
*       then get his due).
*
*         An encrypted file has the following format:
*         The 1st 8 bytes are:
*              1) :05, a signal that an encrypted file format follows.
*              2) count of serial numbers in the :05 record
*              3) 6 bytes of random number rolled explicitly for this file
*         The Count specified in the :05 record tells how many 8-byte
*         serial numbers follow; the serial number of the computer
*         attempting to execute the file must be in this list or the
*         loader will refuse to load it. The rest of the file is in
*         standard SDOS load record formats, but it is encrypted using a
*         key which is a secret function of the entire contents of the
*         :05 record.  The secret function is actually a cascaded
*         decryption of all the bytes in the :05 record, with a Secret
*         key being used to start the process, each result being used as
*         the key to decrypt the next round, and the final result being
*         used as the decryption key for the rest of the file. Since the
*         encryption key is the result of a secret function, only the
*         SDOS loader can decrypt the file. Changing the contents of the
*         :05 record to include a different serial number will allow the
*         Loader to attempt to load the file, but it will fail because it
*         will decrypt using the wrong key. Even if the theif obtains the
*         key to crack this file, since the key depends on the random
*         number chosen explicitly for this copy of the file, the key
*         will do him no good when attempting to steal the next file.
*         It has been proposed that the serial number be added to the
*         object text of the file, so that a stolen copy could be traced
*         back to the originating machine, but the philosophy was that ANY
*         program could simply be encrypted without affecting its
*         execution, and this condition would require the program to know
*         that it must not step on a certain area of memory.
          page
*         The secret key generation should be done with a great deal of
*         trickiness to prevent it from being known. A nasty trick is to
*         push the code that generates the secret key into the stack, and
*         execute it there. The data source is stored in some non-trivial
*         encrypted format, and decrypted on the fly as it pushed into the
*         stack. Ah well, this is not what we do.
*
systemdefs equ 1 ; want all the system definitions
         include sdos11defs.asm   ; precious defs
         page
         title             SerialNumber.Sys Module
         ifund ColorComputer
ColorComputer equ 0
         fin
         org   $20           Page zero locations for SERIALNUMBER.SYS
FromPointer rmb 2            Source pointer
ToPointer   rmb 2            Target pointer

         if    ColorComputer
         Org   $1100         Pick someplace not on top of CoCo vectors
         else
         Org   $100          Load address of SERIALNUMBER.SYS
         fin

SerialNumber.Sys ; 1st byte of SerialNumber.Sys module

DoSyscall ; Execute the Syscall given by (x), Take error exit if error
         Jsr   syscall$
         bcs   Fatalerror
         rts

ErrorExit ; Syscall to do an error exit
         fcb syscall:ErrorExit,ErrorExit:Sclen
ErrorCode      fdb           changed to the actual error code, dummy!

ErrWrongSerialNumber
         Ldx   #Err:SerialNoWrong
FatalError ; (x) contains error code, go tell user via SDOS
         Stx   ErrorCode     Now, what could be more obvious?
*        Zero user space so phony Defaultprogram can't peek at memory
*        just because SERIALNUMBER.SYS was forced to take an error exit
*        This maneuver isn't technically necessary since the SDOS loader
*        will re-zero memory when it loads the DEFAULTPROGRAM, since
*        it is extremely improbable that DEFAULTPROGRAM has been encrypted
*        with the same key as SERIALNUMBER.SYS
*        Note that since the first CHAIN executed after this is very
*        unlikely to have the SDOS long-term enabling code hidden in it,
*        SDOS is unlikely to run long-term after it prints this error.
         Ldx syscall$+1  Where to start zeroing
ZapMemory
         clr   ,-x
         cpx   #SerialNumber.Start Can't destroy below this point
         bne   ZapMemory
         Ldx   #ErrorExit
         Jsr syscall$
         Bcs   *             gak die

SERIALNUMBER.START ; Entry here means SDOS successfully loaded SERIALNUMBER.SYS
         ldx   syscall$+1    a little paranoia never hurt anyone, reset stack
         txs
         jsr   Selftestchecksum     compute checksum over self
         bne   Fatalerror    b/ no, give error!
*
*        We assume that the ROM is safe since the SDOS loader had to verify it...
*        before it would decrypt and load SERIALNUMBER.SYS
*
         if     0
         ldx   #0            funny way to get $fffe
         leax  -2,x
         ldd   0,x           grab copy of restart vector
         sts   0,x           make sure restart vector is in ROM
         cmpd  0,x           is it?
         bne   errwrongserialnumber b/ nope, quit now!
         ldx   0,x           pointer to Jump instruction
         ldd   3+0,x         take snapshot of serial number
         std   serialnumber
         ldd   3+2,x
         std   serialnumber+2
         ldd   3+4,x
         std   serialnumber+4
         ldd   3+6,x
         std   serialnumber+6
         sts   3+6,x         make sure serial number is in ROM
         stx   3+2,x         make sure serial number is in ROM
         std   3+0,x         make sure serial number is in ROM
         stx   3+4,x         make sure serial number is in ROM
         cmpd  3+6,x         (check that "ROM" contents did not change)
errwrongserialnumberj
         bne   errwrongserialnumber b/ changed, that stuff is not ROM!
         ldd   serialnumber
         cmpd  3+0,x
         bne   errwrongserialnumberj
         ldd   serialnumber+2
         cmpd  3+2,x
         bne   errwrongserialnumberj
         ldd   serialnumber+4
         cmpd  3+4,x
         bne   errwrongserialnumberj
*
*        Well, it appears the ROM isn't phony.
*        To be really paranoid, we should Open SERIALNUMBER.SYS and
*        fetch the serial number contained therein and compare to ROM;
*        the value in the file should be printed.  We currently assume
*        that if SERIALNUMBER.SYS gets loaded, the SDOS loader has made
*        all of these checks, and so we can simply print the number in the ROM.
*
         else
         ldx   #GetSerialNumber    Fetch the serial number of this machine
         jsr   Dosyscall
         fin
*        Now modify the "1st Chain" syscall block to hold...
*        the magic SDOS enabling key
*
         ldx   #Fillwith3NOPs fetch "pointer" to syscall block to init
         ldd   scrambledenablekey get enable key (in scrambled form)
         subd  scramblevalue unscramble it (yes, I'm REALLY paranoid!)
         std   ChainInitialize.Sys-Fillwith3NOPs+scblk:params,x
*              store enabling key into 1st CHAIN syscall block
*        Now, SDOS will run long-term when CHAINed to.
*
*        This is a good time to print the Serial Number contained in the ROM
*
         ldx   #Serialnumber for each serial number byte...
convertserialtoascii ; convert serial number byte to ascii and push
         lda   ,x+           get a byte of the serial number
         jsr   hextod        convert (a) to ascii hex bytes in (d)
         pshd
         cpx   #Serialnumber+8 all bytes converted?
         bne   convertserialtoascii b/ no, go do another
         ldx   #Serialascii+16 target address for rightmost ascii hex byte
saveserialascii ; pop a hex byte pair and save in ascii hex buffer
         puld
         std   ,--x          save in ascii buffer for serial number bytes
         cpx   #Serialascii  done?
         bne   saveserialascii b/ no
Fillwith3NOPs ; filled with 3 NOPs when customer name is collected
         Jmp   FirstTime     Go do first-time-only work
         ldx   #PrintSerialNumber Tell the world which CPU this is...
         jsr   Dosyscall
SerialNumberDone ; SerialNumber.Sys work is complete, let SDOS run normally!
         ldx   #OpenInitialize.do  See if Initialize.do exists
         jsr   syscall$
         bcc   DoInitialize.do
         cpx   #Err:FileNotFound  Is this the problem ?
         lbne  FatalError   No, go tell user! (and SDOS will die...)
         bra   SerialNumberChain

ScrambledEnableKey ; 2 bytes that allow SDOS to run reliably, long term
;        These two bytes must be passed in the Syscall:params part...
;        of the 1st chain performed by Serialnumber.sys
;        Note: the value stored in these bytes is scrambled,
;        so the enabling code is never to be found in static memory
;        on the faint chance that somebody breaks the protection
         if    m6800!m6801
         fdb   $3139-3141    "INS/RTS" sequence does it for 6800/6801
         else  (m6809)
         fdb   $6139-3141    "(leas)...1,S\RTS" does it for 6809
         fin

DoInitialize.do ; Set up Initialize.do as a "DO" file
         ldx   #Close0         Set up Initialize.do as "DO" file
         jsr   Dosyscall
         ldx   #CreateLogConsole
         jsr   Dosyscall
         clr   OpenInitialize.do+Scblk:params  Set channel # to 0
         ldx   #OpenInitialize.do
         jsr   Dosyscall
         ldx   #ResetLastError to zero, so Do file doesn't abort!
         jsr   Dosyscall
SerialNumberChain ; Chain to Initialize.sys or Defaultprogram
*
*        Note: This point is the first "official" Syscall:Chain to occur
*        (the routine that loaded SERIALNUMBER.SYS is a special case)
*        The first Chain invoked must have a special SDOS enabling key...
*        built into it, or SDOS will NOT operate over a long period of time.
*        Furthermore, to prevent a phony Initialize.sys or Defaultprogram
*        from peeking at the memory image of this program, the first Chain
*        must also zero the user space.  This happens automatically
*        since the SDOS loader zeros the user space if the decryption
*        key of the last file to be loaded doesn't match that of the current
*        file being loaded, which is virtually certain for SERIALNUMBER.SYS
*
*        This code also defuses the 12 hour time-bomb in SDOS...
*        which is intended to remind the user that SDOS is not registered
*
         ldx   #Delay0ticks    Defuse 12 hour time bomb
         jsr   dosyscall
         ldx   #KillEnable     Kill enable so Chained-to module doesn't...
         jsr   dosyscall       inherit Killproof properties!
         ldx   #ChainInitialize.sys  Try to pass control to Initialize.sys
         jsr   syscall$
         bcc   *               This cannot happen!  (too bad if it does...)
         cpx   #Err:NoSuchProgram does it not exist?
         lbne  FatalError      B/ Initialize.sys exists, but is sick
         ldx   #Exit           No Initialize.sys; so just exit...
         jsr   Dosyscall       Exit erases memory
*        I swear it cannot get here!
         page

checksumbyte   fcb           checksumvalue Recompute this after assembling
* The algorithm used to compute the checksum makes it extremely hard
* to get this byte right without a computer program.  See code
* for COMPUTEPROPERCHECKSUM at the end of this listing.

SelfTestChecksum ; compute checksum over self.
; with (A) holding 0 if checksum is correct.
         clra                checksum ourselves, to prevent meddlers...
         ldx   #Serialnumber.sys from damaging the encrypted file...
checksumloop   ; and accidentally defeating the protection
         asla                this works nice...
         adca  0,x
         inx
         cpx   #Serialnumber.sysend checksummed all bytes ?
         bne   checksumloop
         ldx   #Err:selftestcksum assume we're screwed up...
         tsta                is checksum zero, as expected ?
         rts

Delay0ticks ; Syscall to delay for 0 clock ticks, defusing 12 hour bomb
         fcb   Syscall:Delay,Delay:sclen
         fdb   0               0 delay required

KillEnable ; Syscall to disable "Killproof"
         fcb   syscall:KillEnable,KillEnable:sclen

Exit ; Syscall block to exit if Chain to initialize.sys fails
         fcb   syscall:Exit,Exit:Sclen

ChainInitialize.Sys ; Syscall to chain to Initialize.Sys
         fcb   syscall:Chain,Chain:Sclen
         fcb   changed,changed to long-term SDOS enabling key
         fdb   Initialize.sys,Initialize.sysend-Initialize.sys
         fdb   Changed    = known length of name of defaultprogram
         fdb   scratch,4
Initialize.sys
         fcc   "Initialize.sys"
Initialize.sysend

OpenInitialize.do ; Syscall to open Initialize.do on channel 1
         fcb   syscall:Open,Open:sclen
         fcb   1,ignored
         fdb   Initialize.do,Initialize.doEnd-Initialize.do
         fdb   Changed     to 2
         fdb   Scratch,4
Initialize.do
         fcc   "Initialize.do"
Initialize.doEnd

ResetLastError ; syscall to set error code to zero
         fcb   syscall:SetError,SetError:sclen
         fdb   0

Close0 ; Syscall to close channel 0
         fcb   syscall:Close,Close:Sclen,0

CreateLogConsole ; Syscall to CreateLog to the Console:
         fcb   syscall:CreateLog,CreateLog:Sclen
         fcb   ignored,ignored
         fdb   Console,ConsoleEnd-Console
         fdb   Changed
         fdb   Scratch,4
Console
         fcc   "Console:"
ConsoleEnd

GetSerialNumber ; Syscall to fetch Serialnumber of computer
         fcb   syscall:GetSerialNumber,GetSerialNumber:Sclen
         fcb   ignored,ignored
         fdb   ignored,ignored
         fdb   Changed
         fdb   SerialNumber,SerialNumber:Size
         page
ScrambleValue ; This value is used to unscramble the "ScrambledEnableKey"
         fdb   -3141         = pi * 100 (a "random" number)

Hextod ; Convert (A) to Ascii Hex in (a,b)
         tab                 duplicate byte so we can split into nibbles
         lsra                move upper nibble to lower part of (a)
         lsra
         lsra
         lsra
         andb  #$F           extract lower nibble
         adda  #'0           convert (a) to hex digit
         cmpa  #'9           valid digit?
         bls   Hextod1       b/ yep.
         adda  #'A-10-'0     no, convert to A-F hex.
Hextod1
         addb  #'0           convert (b) to hex ascii
         cmpb  #'9           valid digit?
         bls   Hextod2       b/ yep.
         addb  #'A-10-'0     no, convert to A-F hex
Hextod2
         rts

PrintSerialNumber
         fcb   syscall:Writea,Writea:Sclen
         fcb   0,ignored
         fdb   SerialNumberMessage,SerialNumberMessageEnd-SerialNumberMessage

         if    ColorComputer
SerialNumberMessage
         fcc   "Licensed for use only by"
         fcb   ascii:cr
CustomerName ; This is where the customer places his name
         fcc   "                                        "
CustomerNameEnd fcb Ascii:cr
         fcc   "only on a single computer with"
         fcb   ascii:cr
CPUSerialNumberMsg
         fcc   "Serial Number "
SerialAscii ; place where Ascii hex version of serial number is placed
         fcc   "XXXXXXXXXXXXXXXX"
         fcb   ascii:cr
SerialNumberMessageEnd
         else
SerialNumberMessage
         fcc   "This copy licensed for use only by "
CustomerName ; This is where the customer places his name
         fcc   "                                        "
CustomerNameEnd fcb          Ascii:cr
         fcc   "only on the single computer with "
CPUSerialNumberMsg
         fcc   "CPU Serial Number "
SerialAscii ; place where Ascii hex version of serial number is placed
         fcc   "XXXXXXXXXXXXXXXX"
         fcb   ascii:cr
SerialNumberMessageEnd
         fin

*****************************************************************************
*****  All code beyond this label is NOT re-written to SerialNumber.SYS *****
SerialNumber.sysend ; last place that needs to be saved by Serial Number.sys program
*****************************************************************************
         page
PleaseWait ; Syscall to print "Please wait..."
         fcb   syscall:writea,Writea:Sclen
         fcb   0,ignored
         fdb   PleaseWaitMsg,PleaseWaitMsgEnd-PleaseWaitMsg

PleaseWaitMsg
         if    ColorComputer
         fcc   "SDOS will be ready..."
         fcb   ascii:cr
         fcc   "in 30 minutes, please wait..."
         else
         fcc   "SDOS will be ready in 30 minutes, please wait..."
         fin
         fcb   ascii:cr
PleaseWaitMsgEnd

DelayLongTime ; Customer ID is blank, idle a long time...
*        to make sure customer soon gets tired of not entering his name
         ldx   #PleaseWait   let user know what's happening...
         jsr   dosyscall
         ldx   #15*60*60     Set delay to 15 minutes...
         stx   Delay0Ticks+Delay:Period which keeps 12 hour time bomb fused
         ldx   #Delay0Ticks  Delay for 15 minutes...
         jsr   dosyscall
         jmp   SerialNumberdone  go do misc, then wait 15 minutes to start SDOS
*
*        First time logic starts here
*
Firsttime
         if    ColorComputer
         ldx   #PrintCSDHelloPart1    ; say HELLO from CSD
         jsr   DoSyscall
         jsr   AcceptLoop             ; wait for RETURN key
         ldx   #PrintCSDHelloPart2    ; tell 'em about support
         jsr   DoSyscall
         jsr   AcceptLoop             ; wait for RETURN key
         fin
         ldx   #PrintExplanation
         jsr   Dosyscall     Show the first time message
         ldx   #PrintCPUSerialNumber Tell the user what the serial number is
         jsr   dosyscall
*
*        Ask for customer name
*
CollectCustomerName
         ldx   #PrintEnterCustomerName
         jsr   Dosyscall     ask for customer name again
         ldx   #AcceptCustomerName
         jsr   Acceptandchopinput
         ldd   CustomerNameLength
**** this code should have lotsa embedded oddball constants
** to muck up any would-be disassemblers (assuming they get this far!)
         cmpd  #1            Empty name?
         beq   DelayLongTime b/ Yes, wait a looooooong time before done
         addd  #CustomerName-1 Pad customer name with blanks
         tdx
         ldaa  #Ascii:space
BlankpadCustomerName
         sta   ,x+
         cpx   #CustomerNameEnd
         bne   BlankpadCustomerName
*
*        Compute secret function of customer name and ROM serial number
*        = cascaded encryption of SDOSSecretKey, ROM serial number,
*        and 40 byte blank/tab stripped, uppercase version of Customer id
*
*        First, strip the customer name
*
         Ldx   #CustomerName  where to copy from
         stx   frompointer
         ldx   #Stripbuffer   where to put stripped result
         stx   topointer
StripLoop ; Strip a byte of customer name
         clra                 Assume source is exhausted: pad with zeros
         ldx   frompointer    where to get bytes from
         cpx   #CustomerNameEnd source exhausted?
         beq   Striploop1     b/ yes
         lda   ,x+            fetch byte from customer name buffer
         stx   frompointer    save updated customer buffer scan pointer
         cmpa  #ascii:space   is it a blank?
         beq   Striploop      yes, ignore it
         cmpa  #ascii:ht      a tab?
         beq   Striploop      yes, ignore them, too!
Striploop1     ; (a)= character from customer name buffer
         cmpa  #'A+32         lower case alpha?
         bcs   Striploop2     b/ no, leave it alone
         cmpa  #'Z+32         ...?
         bhi   Striploop2     b/ no, leave it alone
         suba  #32            yes, fold to upper case alpha
StripLoop2     ; (a) = uppercase version of character from customer name buffer
         ldx   topointer      where to put next character
         sta   ,x+
         stx   topointer      save where to put next character
         cpx   #Stripbuffer+CustomerNameEnd-CustomerName Done?
         bne   Striploop      b/ no, go fetch another character from name
         ldx   #SDOSSecretKey Start cascade to compute secret function
         jsr   Setkey         = initial value for cascade
         ldx   SerialNumber   copy ROM serial number to Write buffer...
         stx   Buffer         to initialize the encryption cascade
         ldx   SerialNumber+2
         stx   Buffer+2
         ldx   SerialNumber+4
         stx   Buffer+4
         ldx   SerialNumber+6
         stx   Buffer+6
         ldx   #Buffer   = 1st cascade step
         jsr   Encrypt
         jsr   Setkey
         ldx   #Stripbuffer+0*8 = 2nd cascade step
         jsr   Encrypt        do cascade step
         jsr   setkey
         ldx   #Stripbuffer+1*8 = 3rd cascade step
         jsr   Encrypt        do cascade step
         jsr   setkey
         ldx   #Stripbuffer+2*8 = 4th cascade step
         jsr   Encrypt        do cascade step
         jsr   setkey
         ldx   #Stripbuffer+3*8 = 5th cascade step
         jsr   Encrypt        do cascade step
         jsr   setkey
         ldx   #Stripbuffer+4*8 = 6th cascade step
         jsr   Encrypt        do cascade step
*
*        Now, secret function of Customer name and ROM serial number...
*        is stored in SecretFunctionRestult
*
*
* The following code accepts a Hex number, and compares it with the secret
* function of both the SerialNumber (from the ROM) and the Customer name given.
* If it matches, SerialNumber.Sys accepts the User name; otherwise, it
* is rejected.  The user must obtain the number from SD; he may do this
* over the phone or by mail, but he must be in possesion of the number
* at the instant he wishes to enter the User Id.
* The secret function does NOT depend on the case (upper/lower)
* or distance between (blanks and tabs ignored) of characters
* in the customer's name; this prevents many transcription errors.
*
AskforRegistration ; ask user for SD registration code
         Ldx   #Printthiswillappear  Show user how his name will appear
         Jsr   Dosyscall
         Ldx   #PrintSerialNumber    Print initial message again
         Jsr   DoSyscall
         Ldx   #PrintEmptyline
         Jsr   Dosyscall
         Ldx   #RequestVerification
         Jsr   DoSyscall
         ldx   #AcceptHex    ask for Hex input
         jsr   Acceptandchopinput
         lda   Hexbuffer     did user enter simply Ascii:Cr?
         cmpa  #ascii:cr     (namely, "oops, the customer id is wrong!")
         lbeq  CollectCustomerName b/ yes, let him re-enter the customer id.
         ldx   AcceptHex+Scblk:rplen Proper number of characters entered?
         cpx   #17            = 16 digits + activation?
         bne   InvalidRegistration b/ no, go complain.
         ldx   #SecretFunctionResult What to compare to entered registration number
         stx   topointer
         ldx   #Hexbuffer  scan the Hex digits, and convert to binary
         stx   frompointer
         ldb   #8            = # digit pairs
Registrationcheckloop ; Convert a pair of hex digits and compare to secret fn
         ldx   frompointer   = next byte pair to convert from ascii to hex
         lda   ,x+           get left character of byte pair
         jsr   convertasciitohex
         bcs   Nothexinput   b/ not a hex digit
         asla                shift to left nibble
         asla
         asla
         asla
         sta   Hexbuffer     save partial value
         lda   ,x+           get right character of byte pair
         jsr   convertasciitohex
         bcs   Nothexinput   b/ not a hex digit
         adda  Hexbuffer     combine nibbles to form complete byte
         stx   frompointer   save pointer for next iteration
         ldx   topointer     what to compare against
         cmpa  0,x           entered value match secret function byte?
         bne   RegistrationIncorrect b/ no, go bitch!
         inx                 advance pointer
         stx   topointer
         decb                = # bytes left to process
         bne   Registrationcheckloop
         bra   Registrationcodeverifies

Nothexinput ; Registration code entered is not entirely hexadecimal
RegistrationIncorrect ; Registration code doesnt match secret function
InvalidRegistration ; Registration code is malformed or incorrect
         ldx    #PrintBadRegistration
         jsr    dosyscall     let user know in no uncertain terms he screwed up.
         If     ColorComputer
         jsr    AcceptLoop    give user chance to see what is wrong
         fin
         jmp    askforregistration  and ask him again

PrintBadRegistration ; syscall to print out bad registration message
         fcb    syscall:writea,Writea:sclen
         fcb    0,ignored
         fdb    BadRegistrationMsg,BadRegistrationMsgEnd-BadRegistrationMsg

BadRegistrationMsg
         if     ColorComputer
         fcc    "Registration code entered is"
         fcb    ascii:cr
         fcc    "malformed or incorrect."
         fcb    ascii:cr
         fcc    "(Push RETURN to continue)"
         else
         fcc    "Registration code entered is malformed or incorrect."
         fin
         fcb    ascii:cr
BadRegistrationMsgEnd
         page
*
*        Value entered by user matches value computed, set the User ID!
*
RegistrationCodeVerifies ; Customer entered correct registration code
         ldx   #FriendlyReminder Tell user of success
         jsr   Dosyscall     and give him something to read whilst we do stuff
         if    m6800!m6801
         ldaa  #$01          = NOP opcode
         else  (m6809)
         ldaa  #$12          = 6809 NOP
         fin
         staa  Fillwith3NOPs  set up so next time it will print # and exit
         staa  Fillwith3NOPs+1
         staa  Fillwith3NOPs+2
*
*        now compute new checksum over Serialnumber.sys module
*
Computenewchecksum ; loop back here until checksum is correct
         inc   checksumbyte   try this value to see if it works
         jsr   SelfTestChecksum go compute trial checksum
         bne   Computenewchecksum b/ no good, try another
         ; Success! (assured eventually)  Checksumbyte is correct once again.
*
*        Now write this program back to the disk with the embedded name!
*
         ldx   #OpenSERIALNUMBER.SYS Re-open the file...
         jsr   Dosyscall     so we can steal the 1st 16 bytes!
         ldx   #ReadFileDate Get date of SERIALNUMBER.SYS
         jsr   Dosyscall
         ldx   #Readfirst16bytes So we compute proper encryption key
         jsr   Dosyscall
         ldx   #Close1       close old SerialNumber.sys file
         jsr   Dosyscall
         ldx   #OpenClock    and set the time so SDOS allow us to update
         jsr   Dosyscall     SERIALNUMBER.SYS, and the timestamp will be right
         ldx   #ForceFeedClock  to date of SERIALNUMBER.SYS
         jsr   Dosyscall
         ldx   #OpenSERIALNUMBER.SYS Write registered SERIALNUMBER.SYS...
         jsr   Dosyscall             back into original file to conserve disk space
         ldx   #Write1st16bytes Write out the 1st 16 bytes
         jsr   Dosyscall
         ldx   #SDOSSecretKey   Use this as initial key to start cascade
         jsr   setkey
******* perhaps we can distribute the code that sets up the initial key?
         ldx   #Type5RecordHeader
         jsr   decrypt       do 1st step of decryption cascade
         jsr   setkey        use as key for 2nd step
         ldx   #Type5RecordSerialNumber
         jsr   decrypt       do 2nd step of decryption cascade...
         jsr   setkey        This is THE encryption key to use
         if    m6800
         ldaa  #1            get Type 1 record head
         elseif m6801
         ldaa  #3            get Type 1 record head for 6801
         else  (m6809)
         ldaa  #2            get Type 1 record head for 6809
         fin
         jsr   writebyte     write byte to SERIALNUMBER.SYS file
         ldd   #SerialNumber.Start Get the start address
         jsr   writeword     write word to SERIALNUMBER.SYS file
         ldd   #\SerialNumber.Start The complemented address
         jsr   writeword
         ldaa  #3            Only one load record...
         jsr   writebyte
         ldd   #SerialNumber.Sys This is the lowest point to write
         jsr   writeword
         ldd   #SerialNumber.sysend-SerialNumber.sys # of bytes to write
         jsr   writeword
         ldx   #SerialNumber.Sys = address of 1st byte to write
Writeobject ; write object bytes to SerialNumber.sys
         lda   ,x+           fetch next byte to write
         pshx
         jsr   writebyte
         pulx
         cpx   #SerialNumber.sysend
         bne   Writeobject
         jsr   writedone     finish writing bytes to the file
         ldx   #TruncateFile    now chop file off here, rest is excess baggage
         jsr   DoSyscall
         ldx   #SetWriteProtect on the file
         jsr   Dosyscall
         ldx   #Close1
         jsr   dosyscall
****** Heaven help the guy who gets a disk write error here! ******
         ldx   #ZeroClock       so Defaultprogram will ask for correct TIME
         jsr   Dosyscall
         jmp   SerialNumberdone

WriteWord ; Subroutine to put (D) to channel 1 in encrypted format
         pshb                save right half
         bsr   writebyte
         pula
WriteByte ; Subroutine to write (a) to channel 1 in encrypted format
         ldx   bufferpointer put byte into the buffer
         sta   ,x+
         cpx   #buffer+8     is buffer now full?
         bne   WriteByte1    b/ no, all done!
         ldx   #buffer       yes, encrypt it!
         jsr   encrypt
         ldx   #WriteBuffer  write the buffer contents to the disk
         jsr   Dosyscall
         ldx   #buffer
WriteByte1 ; (X) contain new buffer pointer
         stx   bufferpointer
         rts
WriteDoneLoop ; Must continue to fill buffer
         clra                generate a filler byte
         bsr   WriteByte
WriteDone ; Finish writing final buffer to channel 1 in encrypted format
         ldx   bufferpointer is buffer empty?
         cpx   #buffer
         bne   Writedoneloop b/ buffer has something in it...
         rts

WriteBuffer ; Syscall to write buffer contents
         fcb   syscall:Writeb,Writeb:sclen
         fcb   1,ignored
         fdb   Buffer,8

         if    ColorComputer

PrintCSDHelloPart1 ; Syscall block to print Computer Systems distributor HELLO.
         fcb   syscall:Writea,Writea:sclen
         fcb   0,ignored
         fdb   CSDHelloMessagePart1
         fdb   CSDHelloMessagePart1End-CSDHelloMessagePart1

; following line is reference for 32 character wide Color Computer screen
;fcc "123456789#123456789#123456789#12"
CSDHelloMessagePart1
 fcc "   Hello.  Computer Systems"
 fcb ascii:cr
 fcc "Distributors is pleased you"
 fcb ascii:cr
 fcc "have chosen SDOS to help solve"
 fcb ascii:cr
 fcc "your data processing problems."
 fcb ascii:cr
 fcc "Due to the very low cost of"
 fcb ascii:cr
 fcc "this software package, and its"
 fcb ascii:cr
 fcc "complexity, we can only afford"
 fcb ascii:cr
 fcc "to handle VERBAL questions for"
 fcb ascii:cr
 fcc "a fee.  We welcome WRITTEN"
 fcb ascii:cr
 fcc "questions and will answer them"
 fcb ascii:cr
 fcc "as quickly as possible, free,"
 fcb ascii:cr
 fcc "if you enclose a SASE."
 fcb ascii:cr
 fcc "(Push RETURN for more)"
 fcb ascii:cr
CSDHelloMessagePart1End

PrintCSDHelloPart2 ; Syscall block to print Computer Systems distributor HELLO.
         fcb   syscall:Writea,Writea:sclen
         fcb   0,ignored
         fdb   CSDHelloMessagePart2
         fdb   CSDHelloMessagePart2End-CSDHelloMessagePart2

CSDHelloMessagePart2
;fcc "123456789#123456789#123456789#12"
 fcb ascii:ff
 fcc "Now to a piece of once-only"
 fcb ascii:cr
 fcc "business. You must register"
 fcb ascii:cr
 fcc "this copy of SDOS with CSD"
 fcb ascii:cr
 fcc "to have a valid warranty."
 fcb ascii:cr
 fcc "To do this, you must send the"
 fcb ascii:cr
 fcc "buyer's name (40 characters"
 fcb ascii:cr
 fcc "max) along with the software"
 fcb ascii:cr
 fcc "serial number to CSD.  This"
 fcb ascii:cr
 fcc "may be done by mail; CSD will"
 fcb ascii:cr
 fcc "return a 16 character"
 fcb ascii:cr
 fcc "Registration code."
 fcb ascii:cr
 fcc "(Push RETURN for more)"
 fcb ascii:cr
CSDHelloMessagePart2End
         fin

PrintExplanation ; Syscall block to print out explanation message
         fcb   syscall:Writea,Writea:sclen
         fcb   0,ignored
         fdb   ExplanationMessage,ExplanationMessageEnd-ExplanationMessage

ExplanationMessage ; Message telling customer how to enter his name.
         if    ColorComputer
;fcc "123456789#123456789#123456789#12"
 fcb ascii:ff
 fcc "You must enter the buyer's"
 fcb ascii:cr
 fcc "name (EXACTLY as sent to CSD),"
 fcb ascii:cr
 fcc "and the Registration Code here."
 fcb ascii:cr
 fcc "If you do not have your Code"
 fcb ascii:cr
 fcc "yet, push RETURN when asked"
 fcb ascii:cr
 fcc "for the buyer's name; SDOS"
 fcb ascii:cr
 fcc "will be ready after a delay."
 fcb ascii:cr
 fcc "This is "
 fcb ascii:cr
         else
 fcc "    Hello.  Software Dynamics is pleased you have chosen SDOS to help you"
 fcb ascii:cr
 fcc "solve your data processing problems.  We welcome any questions or comments"
 fcb ascii:cr
 fcc "you may have, and will try to solve any problems you may have as quickly as"
 fcb ascii:cr
 fcc "possible.  Naturally, you should direct most questions initially to your"
 fcb ascii:cr
 fcc "vendor, as he will probably understand your problems and hardware"
 fcb ascii:cr
 fcc "configuration somewhat better than we will, and can therefore answer most"
 fcb ascii:cr
 fcc "of your questions directly."
 fcb ascii:cr
 fcc "    Now to a piece of once-only business. You must register this copy of"
 fcb ascii:cr
 fcc "SDOS with SD in order for the warranty to be valid.  To do this, you must"
 fcb ascii:cr
 fcc "send the name of the organization or person (40 characters maximum)"
 fcb ascii:cr
 fcc "purchasing the software, along with the computer serial number, to Software"
 fcb ascii:cr
 fcc "Dynamics.  This may be done by (air)mail or telephone; Software Dynamics"
 fcb ascii:cr
 fcc "will return a 16 character Registration code to you on receipt of this"
 fcb ascii:cr
 fcc "information.  This program will ask you to enter the name of the purchaser"
 fcb ascii:cr
 fcc "(EXACTLY as sent to Software Dynamics), and the Registration Code to"
 fcb ascii:cr
 fcc "validate the name. Once entered, that name will be displayed every time SDOS"
 fcb ascii:cr
 fcc "is 'booted' into the memory of the computer after a Reset, and SDOS will"
 fcb ascii:cr
 fcc "then be ready for immediate use. Until the name is entered and validated,"
 fcb ascii:cr
 fcc "this explanation will be displayed after every 'boot'. If you have not yet"
 fcb ascii:cr
 fcc "received a registration code from SD, then you should simply enter <RETURN>"
 fcb ascii:cr
 fcc "when asked for the name; SDOS will be ready for use after a short delay."
 fcb ascii:cr
 fcc "This is "
         fin
ExplanationMessageEnd

PrintCPUSerialNumber ; syscall to print CPU serial number
         fcb   syscall:writea,Writea:sclen
         fcb   0,ignored
         fdb   CPUSerialnumbermsg,SerialNumberMessageEnd-CPUSerialnumbermsg

PrintEnterCustomerName ; syscall to print enter customer name message
         fcb   Syscall:writea,Writea:Sclen
         fcb   0,ignored
         fdb   PrintEnterNamemsg,PrintEnterNamemsgEnd-PrintEnterNamemsg

PrintEnterNamemsg
         fcc   "Enter Purchaser Name: "
         if    ColorComputer
         fcb   ascii:cr
         fin
PrintEnterNamemsgEnd
                            
PrintThiswillappear ; syscall to print "this will appear at boot time"
         fcb   Syscall:writea,Writea:Sclen
         fcb   0,ignored
         fdb   PrintThiswillappearmsg,PrintthiswillappearmsgEnd-PrintThiswillappearmsg

PrintThiswillappearmsg
         if    ColorComputer
         fcb   ascii:ff
         fcc   "The following message will"
         fcb   ascii:cr
         fcc   "appear at 'boot' time:"
         else
         fcc   "The following message will appear at 'boot' time:"
         fin
         fcb   ascii:cr
EmptyLine
         fcb   ascii:cr
PrintThiswillappearMsgEnd

PrintEmptyLine ; syscall to output empty line to console:
         fcb   Syscall:writea,Writea:Sclen
         fcb   0,ignored
         fdb   EmptyLine,1

FriendlyReminder ; Syscall to print Reminder message
         fcb   syscall:Writea,Writea:Sclen
         fcb   0,ignored
         fdb   FriendlyReminderMsg,FriendlyReminderMsgEnd-FriendlyReminderMsg

FriendlyReminderMsg
         if    ColorComputer
         fcc   "Proper registration entered."
         fcb   ascii:cr
         fcc   "Your name is now frozen."
         fcb   ascii:cr
         fcb   ascii:cr,ascii:bel
         fcc   "Don't forget to IMMEDIATELY"
         fcb   ascii:cr
         fcc   "make a Backup of your disk!"
         else
         fcc   "Proper registration code entered. Your name is now frozen."
         fcb   ascii:cr
         fcb   ascii:cr,ascii:bel
         fcc   "Don't forget to IMMEDIATELY make a Backup of your System disk!"
         fin
         fcb   ascii:cr
         fcb   ascii:cr
FriendlyReminderMsgEnd

RequestVerification ; Syscall to request verification
         fcb   syscall:Writea,Writea:Sclen
         fcb   0,ignored
         fdb   RequestVerifyMessage,RequestVerifyMessageEnd-RequestVerifyMessage

RequestVerifyMessage
         if    ColorComputer
;        fcc   "123456789#123456789#123456789#12"
         fcc   "Enter registration code, then"
         fcb   ascii:cr
         fcc   "push RETURN key, to register"
         fcb   ascii:cr
         fcc   "name exactly as shown. Push"
         fcb   ascii:cr
         fcc   "just RETURN key if incorrect."
         fcb   ascii:cr
         fcc   "CSD registration code:"
         fcb   ascii:cr
         fcc   ">################<"
         else
         fcc   "Enter the SD registration code between the brackets,"
         fcb   ascii:cr
         fcc   "followed by <RETURN> key, to validate name exactly as shown;"
         fcb   ascii:cr
         fcc   "enter <RETURN> if it is NOT right."
         fcb   ascii:cr
         fcc   "SD registration code: >################<"
         fin
         rpt   17
         fcb   ascii:bs
RequestVerifyMessageEnd

Acceptloop ; Ask for more input until activation seen
         ldx   #Accept1character Input not complete yet, ask for more.
Acceptandchopinput ; Accept input as per syscall block (X)
         jsr   syscall$      perform input request
         bcc   Acceptrts     b/ input was complete in buffer given
         cpx   #Err:attention attention requested?
         beq   Acceptrts     b/ yes, quit with whatever we have so far!
         cpx   #Err:ActivationNotinBuffer did we get complete input?
         beq   Acceptloop    b/ no, eat input until activation seen.
         jmp   FatalError    B/ Something awful happened!?
AcceptRts
         rts

Accept1character ; Syscall to read and ignore a keystroke from operator
         fcb   syscall:Reada,Reada:Sclen
         fcb   0,1         (Linemode)
         fdb   ignored,ignored
         fdb   changed       Size of reply
         fdb   Character,1

Character fcb  changed

SETKEY ; SET "KEY" TO 8 BYTES POINTED TO BY (X)
        LDD       0,X
        STD       KEY+0
        LDD       2,X
        STD       KEY+2
        LDD       4,X
        STD       KEY+4
        LDD       6,X
        STD       KEY+6
        RTS

KEY fdb changed,changed,changed,changed ; ****** until we figure out a better place to put it!

        fcb       $b7         To throw off would-be disassemblers...
SDOSSECRETKEY FCB $4C,$B0,$B7,$4E,$9B,$65,$72,$C9

*
*        Convertasciitohex -- Convert Ascii character in (A) to hex nibble
*        Okrts if Ascii character is Upper/Lower case hex character code
*              Nibble value return in (A)
*        ErrorRts if not hexadecimal Ascii character
*
Convertasciitohex ; Convert Ascii character to binary nibble equivalent
         Cmpa  #'0           A digit?
         bcs   Convertrts    b/ no, just exit.
         cmpa  #'9           ...?
         bls   Convertdigit  b/ yes, go convert
         Cmpa  #'A           Capital Hex?
         bcs   Convertrts    b/ no
         Cmpa  #'F           ...?
         Bls   ConvertUpperHex b/ yes, go convert
         Cmpa  #'A+32        lower case hex?
         bcs   Convertrts    b/ no
         Cmpa  #'F+32        ...?
         bls   ConvertLowerHex b/ yes, go convert
         sec                 Not a hex character
Convertrts
         rts

Convertdigit ; convert digit to nibble
         suba  #'0
         rts                 assert: C=0

ConvertLowerHex ; Convert lower case hex to nibble
         suba  #32           convert lower case hex to upper case hex
ConvertUpperHex ; Convert upper case hex to nibble
         suba  #'A-10
         rts                 assert: C=0
         page
*        Encryption Subroutine
*        Encrypts 8 data bytes pointed to by (X)...
*            according to 8 byte key stored in locations KEY
*        Result is stored as 8 byte string at (X)
*
*        Encryption algorithm is as follows:
*            Result:=ValuetobeEncrypted
*            For bit=64 to 1 -- one iteration per bit
*                If MSB(Result) -- Inspect MSB of Result to decide what to do
*                Then
*                    -- Encrypt step: XOR with KEY
*                    Result:=(Result XOR KEY)*2+[MSB(Result) XOR MSB(KEY)]
*                Else
*                    -- Randomize step: XOR with Randomizing constant
*                    Result:=(Result XOR RNDIZER)*2+[MSB(Result) XOR MSB(KEY)]
*                Fi
*            Endloop -- Encrypted result is in Result
*
*        Decryption algorithm is as follows:
*            Result:=ValuetobeDecrypted
*            For bit=64 to 1 -- one iteration per bit
*                If LSB(Result) XOR MSB(KEY) -- Decrypt LSB to decide how undo
*                Then -- Decrypt step
*                     Result:=[INT(Result/2)+LSB(Result)*2^63] XOR KEY
*                Else -- "UnRandomize" step
*                     Result:=INT([Result XOR (Randomizer*2)]/2)
*                Fi
*             Endloop -- Decrypted result is in "Result"
*
*        Randomizer constant is as follows (random 64 bit string):
*        To prevent loss of this string due to accidental damage to this text,
*        it is repeated 5 times here:
*        %0101010110100110000010101001110011100011010101111010110000111001
*        %0101010110100110000010101001110011100011010101111010110000111001
*        %0101010110100110000010101001110011100011010101111010110000111001
*        %0101010110100110000010101001110011100011010101111010110000111001
*        %0101010110100110000010101001110011100011010101111010110000111001
*
*        The Hex equivalent of the randomizer string is:
*
*        $55 A6 0A 9C E3 57 AC 39
*
*
*        KEY is a buffer of 8 bytes; it should be in page zero for speed!
*        (it could, perhaps, be inserted inline in the encrypt/decrypt code!)
*
Eorall8bytes ; Compute XOR of all 8 bytes
         ldaa  4,x          Why are the index displacements all mixed up?
         eora  7,x          Because it puzzle the hell out of whoever...
         eora  5,x          attempts to dis-assemble it!
         eora  0,x
         eora  2,x
         eora  6,x
         eora  1,x
         eora  3,x           so Most sig byte of result depends on all 64 bits
         rts

EncryptLSB ; Set MSB to XOR of all 8 bytes
*     think about this, someday...
*     Dennis Painter sez: put key just below (S) and run with
*     Interrupts disabled.  Then it cannot be damaged unless an
*     NMI occurs, which will erase it.
Encrypt  ; And now it begins
*        ldab  #64           = # iterations to execute (too slow!)
         ldab  key+7         do 8 + 2 lsb of key iterations
         andb  #3
         addb  #8
         jsr   Eorall8bytes  scramble MSB
         staa  0,x           examine sign bit to decide what to do
         bpl   encryptrandomize b/ MSB is zero, go do randomize step
encryptstep ; MSB is one, perform encryption step
*        EOR current value with key, shift left
         ldaa  7,x           EOR this byte with key byte
         eora  key+7
         asla                shift left, forcing LSB=0
         staa  7,x
         ldaa  6,x           EOR this byte with key byte
         eora  key+6
         rola                shift left, saving carry from byte to right
         staa  6,x
         ldaa  5,x           EOR this byte with key byte
         eora  key+5
         rola                shift left, saving carry from byte to right
         staa  5,x
         ldaa  4,x           EOR this byte with key byte
         eora  key+4
         rola                shift left, saving carry from byte to right
         staa  4,x
         ldaa  3,x           EOR this byte with key byte
         eora  key+3
         rola                shift left, saving carry from byte to right
         staa  3,x
         ldaa  2,x           EOR this byte with key byte
         eora  key+2
         rola                shift left, saving carry from byte to right
         staa  2,x
         ldaa  1,x           EOR this byte with key byte
         eora  key+1
         rola                shift left, saving carry from byte to right
         staa  1,x
         ldaa  0,x           EOR this byte with key byte
         eora  key+0
         rola                shift left, saving carry from byte to right
         bcc   encryptstep0  b/ encrypted MSB is now zero
         inc   7,x           remember that encrypted MSB is a one
encryptstep0 ; encrypted MSB has been recorded in LSB of result
         decb                down count # iterations to perform
         beq   encryptrts    b/ done encrypting
         staa  0,x
         bmi   encryptstep   b/ new MSB is one, go do encrypt step
encryptrandomize ; MSB is zero, perform randomize step
         ldaa  7,x           EOR this byte with randomizer byte
         eora  #$39
         asla                shift left, forcing LSB to be zeroed
         staa  7,x
         ldaa  6,x           EOR this byte with randomizer byte
         eora  #$AC
         rola                shift left, saving carry from byte to right
         staa  6,x
         ldaa  5,x           EOR this byte with randomizer byte
         eora  #$57
         rola                shift left, saving carry from byte to right
         staa  5,x
         ldaa  4,x           EOR this byte with randomizer byte
         eora  #$E3
         rola                shift left, saving carry from byte to right
         staa  4,x
         ldaa  3,x           EOR this byte with randomizer byte
         eora  #$9C
         rola                shift left, saving carry from byte to right
         staa  3,x
         ldaa  2,x           EOR this byte with randomizer byte
         eora  #$0A
         rola                shift left, saving carry from byte to right
         staa  2,x
         ldaa  1,x           EOR this byte with randomizer byte
         eora  #$A6
         rola                shift left, saving carry from byte to right
         staa  1,x
         ldaa  key           EOR MSB of result with MSB of key
         anda  #$80          (Note: this requires that MSB(randomizer) be zero)
         eora  0,x           EOR MSB with MSB of key
         eora  #$55          and EOR rest with randomizer
         rola                shift left, saving carry from byte to right
         bcc   encryptrand0  b/ encrypted MSB=0
         inc   7,x           record that encrypted MSB is 1
encryptrand0 ; encrypted MSB is now saved in LSB of result
         decb                down count # iterations to perform
         beq   encryptrts    b/ done encrypting
         staa  0,x
         bpl   encryptrandomize b/ next bit is zero
         jmp   encryptstep

encryptrts ; encryption is complete
         staa  0,x           store MSB of final result
         jsr   eorall8bytes  scramble lsbyte
         staa  7,x
         rts
         page
*
*        Decrypt -- Undoes Encrypt
*            Decryption key is stored at KEY
*            Block of 8 bytes at (X) is decrypted in place
*
decrypt0 ; "encrypt" bit was 0, right shift current value and EOR with randomizer
         eora  key+0         undo "eora key+0" done by decryptloop
         anda  #$7F          force MSB to be zero
         eora  #$55          EOR with randomizer byte
         staa  0,x           (Note: MSB(Randomizer) must be zero)
         ldaa  1,x           EOR with this byte of key
         rora                shift right, saving carry from left
         eora  #$A6
         staa  1,x
         ldaa  2,x           EOR with this byte of key
         rora                shift right, saving carry from left
         eora  #$0A
         staa  2,x
         ldaa  3,x           EOR with this byte of key
         rora                shift right, saving carry from left
         eora  #$9C
         staa  3,x
         ldaa  4,x           EOR with this byte of key
         rora                shift right, saving carry from left
         eora  #$E3
         staa  4,x
         ldaa  5,x           EOR with this byte of key
         rora                shift right, saving carry from left
         eora  #$57
         staa  5,x
         ldaa  6,x           EOR with this byte of key
         rora                shift right, saving carry from left
         eora  #$AC
         staa  6,x
         ldaa  7,x           EOR with this byte of key
         rora                shift right, saving carry from left
         eora  #$39
         staa  7,x
         decb                down count # iterations
         bne   decryptloop   b/ more iterations to try
         jmp   decryptrts    decryption complete, result is in Result

Decrypt ; Do the encryption in reverse, literally
*        ldab  #64           64 iterations of reverse encryption required
         ldab  key+7         do 8 + 2 lsb of key iterations
         andb  #3
         addb  #8
         jsr   eorall8bytes  unscramble least significant byte
         staa  7,x           do first decryption iteration
decryptloop ; right shift current value and EOR with key
         rora                Shift encrypted "encrypt" bit into carry...
         ldaa  0,x           EOR with this byte of key
         rora                shift right, saving carry from left
         eora  key+0
         bpl   decrypt0      b/ encrypt bit was zero, go do "unrandomize" step
         staa  0,x
         ldaa  1,x           EOR with this byte of key
         rora                shift right, saving carry from left
         eora  key+1
         staa  1,x
         ldaa  2,x           EOR with this byte of key
         rora                shift right, saving carry from left
         eora  key+2
         staa  2,x
         ldaa  3,x           EOR with this byte of key
         rora                shift right, saving carry from left
         eora  key+3
         staa  3,x
         ldaa  4,x           EOR with this byte of key
         rora                shift right, saving carry from left
         eora  key+4
         staa  4,x
         ldaa  5,x           EOR with this byte of key
         rora                shift right, saving carry from left
         eora  key+5
         staa  5,x
         ldaa  6,x           EOR with this byte of key
         rora                shift right, saving carry from left
         eora  key+6
         staa  6,x
         ldaa  7,x           EOR with this byte of key
         rora                shift right, saving carry from left
         eora  key+7
         staa  7,x
         decb                down count # iterations
         bne   decryptloop   b/ more to do
decryptrts
         jsr   eorall8bytes  now unscramble Most significant byte
         staa  0,x
         rts                 decryption complete, result is in Result

AcceptCustomerName ; Syscall to input custer name
         fcb   syscall:Reada,Reada:sclen
         fcb   0,1           ; line mode
         fdb   ignored,ignored write buffer
CustomerNameLength
         fdb   changed       ; reply length
         fdb   CustomerName,CustomerNameEnd-CustomerName

OpenSERIALNUMBER.SYS ; Syscall to open SERIALNUMBER.SYS file
         fcb   syscall:Open,Open:sclen
         fcb   1,ignored
         fdb   SERIALNUMBERSYSNAME,SERIALNUMBERSYSNAMEEND-SERIALNUMBERSYSNAME
         fdb   changed
         fdb   scratch,4     place to put file name (who cares??)

SERIALNUMBERSYSNAME
         fcc   "Serialnumber.sys"
SERIALNUMBERSYSNAMEEND

Scratch  fdb   changed,changed place that is ignored

Close1 ; Syscall block to close channel 1
         fcb   syscall:Close,Close:sclen
         fcb   1

ReadFirst16bytes ; Syscall to read 1st 16 bytes of SERIALNUMBER.SYS file
         fcb   syscall:Readb,Readb:sclen
         fcb   1,ignored
         fdb   ignored,ignored
         fdb   changed       reply length, expected value = 16
         fdb   Type5Record   where to read
         fdb   16            Number of bytes to read

Write1st16bytes ; Syscall block to write 1st 16 bytes of re-encrypted file
         fcb   syscall:Writeb,Writeb:Sclen
         fcb   1,ignored
         fdb   Type5Record,16

Type5Record ; 1st 16 bytes of type 5 record from SerialNumber.sys file
Type5RecordHeader ; 1st 8 bytes of type 5 record from Serialnumber.sys file
         fcb   0,0,0,0,0,0,0,0
Type5RecordSerialNumber ; Serial number from type 5 record in Serialnumber.sys
         fcb   0,0,0,0,0,0,0,0

SerialNumber ; place where copy of serial number bytes are stored temporarily
         fcb   changed,changed,changed,changed
         fcb   changed,changed,changed,changed

bufferpointer  fdb   buffer     pointer into 8 byte buffer

buffer   fcb   0,1,2,3,4,5,6,7   8 bytes to decrypt in

*
AcceptHex ; syscall to read Hex input from the keyboard
         fcb   syscall:Reada,Reada:sclen
         fcb   0,1           Line mode
         fdb   ignored,ignored
         fdb   changed       = Reply length
         fdb   Hexbuffer,17  16 digits + 1 byte of activation character

Hexbuffer fcc  "0123456789ABCDEF"
         fcb   ascii:cr      typical value for this byte

Stripbuffer ; Holds deblanked, uppercase version of customer name
         rmb   CustomerNameEnd-CustomerName

SecretFunctionResult equ Stripbuffer+4*8 ; place where Registration code gen'd

OpenClock ; Syscall to open CLOCK: device
         fcb  Syscall:Open,Open:Sclen
         fcb  3,ignored
         fdb  Clockname,Clocknameend-Clockname
         fdb  changed                  reply length
         fdb  Scratch,2                where to put length of Clock name

ClockName
         fcc  "Clock:"
ClockNameEnd

ForceFeedClock ; Syscall to feed CLOCK: the date of the SERIALNUMBER.SYS file
         fcb  Syscall:Writeb,Writeb:sclen
         fcb  3,ignored
         fdb  SerialNumberDate,6

ZeroClock ; Syscall to zero the clock time
         fcb  Syscall:Writeb,Writeb:sclen
         fcb  3,ignored
         fdb  ZeroDate,6

ZeroDate fcb  0,0,0,0,0,0

ReadFileDate ; Syscall to Read date of old syscal file
         fcb  Syscall:Status,Status:sclen
         fcb  1,SC:GetFileDate
         fdb  ignored,ignored
         fdb  changed                  reply length, expected value = 6
         fdb  SerialNumberDate,6       where to put response

SerialNumberDate ; buffer to hold date of serialnumber file
         fcb  0,0,0,0,0,0

TruncateFile ; Syscall to truncate SERIALNUMBER.SYS file...
; after writing the shortened, registered version back in the original file
         fcb  Syscall:Control,4        this makes SDOS give extra space back to DISKMAP.SYS
         fcb  1,CC:SetFileSize

SetWriteProtect ; Syscall to set write protect bit on new Serialnumber.sys file
         fcb  Syscall:Control,8
         fcb  1,CC:SetFileProt
         fdb  FileProtection,1

FileProtection ; Desired file protection of Registered SerialNumber.sys file
         fcb  Prot::Write
         page
ComputeProperChecksum ; This routine is called to compute the proper
; value of CHECKSUMBYTE after debugging of SERIALNUMBER.SYS is complete.
; It is not ever executed when SERIALNUMBER.SYS operates.
;
; To run it:
;        Load SERIALNUMBER.SYS into memory, giving control to a debugger.
;        Set a breakpoint on COMPUTEPROPERCHECKSUMDONE
;        Start execution at COMPUTEPROPERCHECKSUM
;        When breakpoint hit, (A) holds value to patch into CHECKSUMBYTE.
;
         inc    checksumbyte        try a new value.
         jsr    SelfTestCheckSum    See if it works.
         bne    ComputeProperChecksum b/ it didn't, try another one.
         ldaa   checksumbyte        *** VALUE TO PATCH INTO CHECKSUMBYTE ***
ComputeProperChecksumDone ; PUT BREAKPOINT ON THIS LABEL.
         rti                        Don't ever execute this (here to confuse those who don't have the source)

         end   Serialnumber.start
