; Copyright 1996 Acorn Computers Ltd
;
; Licensed under the Apache License, Version 2.0 (the "License");
; you may not use this file except in compliance with the License.
; You may obtain a copy of the License at
;
;     http://www.apache.org/licenses/LICENSE-2.0
;
; Unless required by applicable law or agreed to in writing, software
; distributed under the License is distributed on an "AS IS" BASIS,
; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
; See the License for the specific language governing permissions and
; limitations under the License.
;
; > $.Source.PMF.osinit

; *****************************************************************************

ExecuteInit ROUT
        Push    R14

       ; Point to OsbyteVars
       ; and initialise them

        BYTEWS  WsPtr

        LDRB    R4, LastBREAK           ; 1 => power-on, 2 => hard
        CMP     R4, #PowerOnReset
        ADREQ   R2, PowerOnResetVars
        ADRNE   R2, HardResetVars

        MOV     R3, #0
        STR     R3, TimerAlpha +0       ; zero both copies of TIME
        STR     R3, TimerAlpha +4
        STR     R3, TimerBeta +0
        STR     R3, TimerBeta +4

        MOV     R1, #32                 ; default FX11 and FX12
        STRB    R1, KeyRepDelay
        MOV     R1, #8
        STRB    R1, KeyRepRate

        MOV     R1, WsPtr               ; start at the beginning
        ADR     R11, ByteVarInitTable

ByteVarInitLoop
        LDRB    R0, [R11], #1           ; copy a byte from table
        STRB    R0, [R1], #1            ; to vars
        TEQ     R1, R2                  ; at end ?
        BNE     ByteVarInitLoop         ; [no, then loop]

        STRB    R3, NoIgnore            ; zero NoIgnore
        STRB    R4, LastBREAK           ; put LastBREAK back

; Initialise buffer pointers

        LDR     R0, =ZeroPage+4*(NBuffers-1) ; index to pointer
        MOV     R1, #0                  ; value to store
        MOV     R2, #NBuffers-1
BuffPtrInitLoop
        STR     R1, [R0, #BuffInPtrs]
        STR     R1, [R0, #BuffOutPtrs]
        SUB     R0, R0, #4
        SUBS    R2, R2, #1
        BPL     BuffPtrInitLoop

; mark printer as dormant

        STR     R1, PrinterActive       ; (R1=0)

; initialise SpriteSize to zero (fixes bug MED-00811)

        LDR     R2, =ZeroPage
        STR     R1, [R2, #SpriteSize]

; Initialise event semaphores

        ADR     R0, EventSemaphores
        ADD     R2, R0, #32
10
        STR     R1, [R0], #4            ; clear all 32 event semaphores
        TEQ     R0, R2
        BNE     %BT10

        STRB    R1, FlashState
        STRB    R1, SerialInHandle      ; zero serial handles
        STRB    R1, SerialOutHandle

; Initialise LatchB and soft copy

;        MOV     R1, #0          ; AND with 0 (was omitted on earlier MOS) ; R1 already zero
        MOV     R0, #0          ; EOR with 0
        BL      UpdateLatchB

        LDR     R1, =ZeroPage
        MOV     R0, #4_3330             ; Assume VGA during osinit
        STRB    R0, [R1, #MonitorLeadType]

        BL      ReadUniqueID

        Push    "r9,r12"
        AddressHAL
        MOV     R0, #0
        CallHAL HAL_TimerDevice
        MOV     R4, R0
        CallHAL HAL_IRQClear            ; clear timer 0 IRQ
        MOV     R0, R4
        CallHAL HAL_IRQEnable           ; enable timer 0 IRQ
        Pull    "r9,r12"

; The RTC driver is later on in the module chain, or missing, for now default the
; system time to UNIX epoch + 1 day (so time() doesn't return -1)
secs0070 *      (86400*(365*70+18))     ; from time() in risc_oslib.c.armsys
        LDR     R7, =(secs0070 * 100)   ; centiseconds LSW
        MOV     R8, #&33                ; centiseconds MSW
        BL      Store5ByteInRealTime

; insert soft key 10
        MOV     R2, #&CA
        BL      RDCHS
        Pull    PC

        LTORG

; *****************************************************************************
;
;       InitHostedDAs - Set up the dynamic areas that we kindly host on
;       behalf of other parts of the OS (SpriteUtils, RamFS, Font Manager)

InitHostedDAs
        Push    "r0-r12, lr"

        ; SpriteArea
        MOV     r0, #SpriteSizeCMOS     ; find out how much spritesize configured
        BL      GetConfiguredSize       ; in: r0 = CMOS address, out: r2 = size

        MOV     r1, #ChangeDyn_SpriteArea ; Area number
        MOV     r3, #-1                 ; Base address dynamic
        MOV     r4, #AreaFlags_Sprites  ; Area flags
        MOV     r5, #16*1024*1024       ; Maximum size (changed from -1, address space preservation)
        ADRL    r6, DynAreaHandler_Sprites ; Pointer to handler
        MOV     r7, #-1                 ; Use base address as workspace ptr
        ADRL    r8, AreaName_SpriteArea ; Title string - node will have to be reallocated
                                        ; after module init, to internationalise it
        BL      DynArea_Create          ; ignore any error, we're stuffed if we get one!

        ; RAMDisc
        MOV     r1, #ChangeDyn_RamFS    ; Area number
        MOV     r3, #-1                 ; Base address dynamic
        ARM_read_ID r4
        AND     r4, r4, #&F000
        CMP     r4, #&A000
        MOVEQ   r4, #AreaFlags_RAMDisc_SA ; Area flags, if StrongARM  (introduced for Ursula)
        MOVNE   r4, #AreaFlags_RAMDisc  ; Area flags
      [ PMPRAMFS
        MOV     r5, #PMPRAMFS_Size*4096
        ORR     r4, r4, #DynAreaFlags_PMP
        MOV     r2, #0
        ORR     r4, r4, #DynAreaFlags_NeedsSpecificPages
        MOV     r9, #0
      |
        MOV     r0, #RAMDiscCMOS        ; find out how much RAM disc configured
        BL      GetConfiguredSize       ; in: r0 = CMOS address, out: r2 = size
        MOV     r5, #128*1024*1024      ; A trade off between nice big disc and complete waste of address space
      ]
        ADRL    r6, DynAreaHandler_RAMDisc ; Pointer to handler
        MOV     r7, #-1                 ; Use base address as workspace ptr
        ADRL    r8, AreaName_RAMDisc    ; Title string - node will have to be reallocated
                                        ; after module init, to internationalise it
        BL      DynArea_Create          ; ignore any error, we're stuffed if we get one!
      [ PMPRAMFS
        ; Currently, physical memory pools must be created with 0 size, then resized afterwards
        MOV     r0, #RAMDiscCMOS        ; find out how much RAM disc configured
        BL      GetConfiguredSize       ; in: r0 = CMOS address, out: r2 = size
        MOVS    r1, r2
        MOV     r0, #ChangeDyn_RamFS
        SWINE   XOS_ChangeDynamicArea
      ]

        ; FontArea
        MOV     r0, #FontCMOS           ; find out how much font cache configured
        BL      GetConfiguredSize       ; in: r0 = CMOS address, out: r2 = size

        MOV     r1, #ChangeDyn_FontArea ; Area number
        MOV     r3, #-1                 ; Base address dynamic
        MOV     r4, #AreaFlags_FontArea ; Area flags
        MOV     r5, #32*1024*1024       ; Maximum size changed from -1 for Ursula (limit address
                                        ; space usage on large memory machines)
        ADRL    r6, DynAreaHandler_FontArea ; Pointer to handler
        MOV     r7, #-1                 ; Use base address as workspace ptr
        ADRL    r8, AreaName_FontArea   ; Title string - node will have to be reallocated
                                        ; after module init, to internationalise it
        BL      DynArea_Create          ; ignore any error, we're stuffed if we get one!

        Pull    "r0-r12, pc"

; *****************************************************************************
;
;       ReadHardCMOSDefaults - Read CMOS values for a hard/power-on reset
;       On entry WsPtr -> BYTEWS

ReadHardCMOSDefaults
        Push    R14

        MOV     R0, #PigCMOS
        BL      Read
        STRB    R0, PrinterIgnore

        MOV     R0, #PSITCMOS
        BL      Read
        TST     R0, #2                  ; NoIgnore bit
        MOVEQ   R1, #0
        MOVNE   R1, #&80
        STRB    R1, NoIgnore

        MOV     R1, R0, LSR #5          ; printer type now in bits 0..2
        STRB    R1, PrinterDrivType

        MOV     R0, #MODETVCMOS
        BL      Read
        MOV     R2, R0, LSR #4          ; bit0:=interlace, bits 1-3 := vertical
        AND     R1, R2, #1
        STRB    R1, TVInterlace
        MOV     R2, R2, LSL #31-3       ; bits 29-31 := vertical
        MOV     R2, R2, ASR #29         ; sign extend
        STRB    R2, TVVertical

        MOV     R0, #DBTBCMOS
        BL      Read
        LDRB    R1, StartOptions
        TST     R0, #&10                ; bit 4 = boot bit
        ORREQ   R1, R1, #8              ; noboot => set bit 3
        BICNE   R1, R1, #8              ; boot => clear bit 3
        STRB    R1, StartOptions

        LDR     R2, =ZeroPage+VduDriverWorkSpace+CursorFlags
        ANDS    R1, R0, #8              ; noscroll bit - put 0 or 1
        MOVNE   R1, #1                  ; in bottom byte of CursorFlags
        STRB    R1, [R2]                ; leave other bytes alone

        MOV     R0, #CountryCMOS        ; read country CMOS and store in var
        BL      Read                    ; but don't bind 'Default' to a fixed
        STRB    R0, Country             ; country at this stage

        BL      SetUpPrinterBuffer
        Pull    PC

; *****************************************************************************
;
;       ReadCMOSDefaults - Read CMOS values for any reset
;       On entry WsPtr -> BYTEWS

ReadCMOSDefaults
        Push    R14

        MOV     R0, #DBTBCMOS
        BL      Read
        TST     R0, #2                  ; NZ => loud
        MOVEQ   R1, #&D0                ; (quiet)
        MOVNE   R1, #&90                ; (LOUD)
        STRB    R1, BELLinfo

        MOV     R0, #StartCMOS
        BL      Read
        MOVS    R1, R0, LSL #(32-5)     ; bit 5 -> carry, bit 4 -> N bit
        MOVPL   R1, #KBStat_NoShiftLock + KBStat_ShiftEnable ; SHCAPS
        MOVMI   R1, #KBStat_NoShiftLock + KBStat_NoCapsLock  ; NOCAPS
        MOVCS   R1, #KBStat_NoShiftLock                      ; CAPS
        TST     R0, #1:SHL:7                                 ; [NO]NUM
        ORRNE   R1, R1, #KBStat_NoNumLock
        BICEQ   R1, R1, #KBStat_NoNumLock
        STRB    R1, KeyBdStatus
        LDR     R11, =ZeroPage+KeyWorkSpace
        BL      UpdateLEDs

        MOV     R0, #SystemSpeedCMOS
        BL      Read
        TST     R0, #&20                ; Cache off when b5 set
        LDR     R2, =:NOT: (MMUC_I + MMUC_C + MMUC_W)
        LDRNE   R1, =0
        LDREQ   R1, =MMUC_I + MMUC_C + MMUC_W
        MOV     R0, #MMUCReason_ModifyControl
        BL      MMUControlSub

        BL      Read_Configd_MonitorType
        LDR     r1, =ZeroPage+VduDriverWorkSpace+CurrentMonitorType ; set current to default
        STR     r0, [r1]

        Pull    R14

; and drop thru to ...

ReadKeyDefaults
        Push    R14

        MOV     R0, #KeyDelCMOS         ; Read the default out of CMOS RAM
        BL      Read                    ; comes back in R0
        STRB    R0, KeyRepDelay

        MOV     R0, #KeyRepCMOS         ; Read the default out of CMOS RAM
        BL      Read                    ; comes back in R0
        STRB    R0, KeyRepRate

        Pull    PC

; *****************************************************************************
;
;       PostInit - Called by Sam after modules have been initialised
;

PostInit ROUT
        Push    R14
        BYTEWS  WsPtr

        SWI     XPortable_ReadFeatures
        BVC     %FT01

        MOV     R0, #0
        MOV     R1, #0
        SWI     XPortable_Speed         ; attempt to make the portable go fast!
        MOVVC   R1, #PortableFeature_Speed
        MOVVS   R1, #0
01
        AND     R1, R1, #(PortableFeature_Speed :OR: PortableFeature_Idle :OR: PortableFeature_Stop)
        LDR     R0, =ZeroPage
        STRB    R1, [R0, #PortableFlags]
        Pull    PC

; *****************************************************************************
;
;       SetUpPrinterBuffer - create the printer buffer

SetUpPrinterBuffer Entry "r1-r3"
        MOV     r0, #PrinterBufferCMOS
        BL      Read
        LDR     r2, =ZeroPage
        LDR     r2, [r2, #Page_Size]
        MULS    r3, r2, r0
        BEQ     %FT10                           ; if zero, then use default area & size

        BL      ClaimSysHeapNode                ; else claim space from system heap
        BVC     %FT20                           ; if no error then OK, else use default
10
        LDR     r2, =PrintBuff                  ; use default buffer
        MOV     r3, #PrintBuffSize
20
        LDR     r0, =ZeroPage
        STR     r2, [r0, #PrinterBufferAddr]
        STR     r3, [r0, #PrinterBufferSize]
        EXIT

; *****************************************************************************
;
;       UpdateLatchB - update latch B and soft copy
;
;       LATCHB := (LATCHB AND R1) EOR R0
;

UpdateLatchB
        Push    "R2, R3, R14"
        PHPSEI                                ; disable IRQ

        LDR     R2, =ZeroPage
        LDRB    R3, [R2, #LatchBSoftCopy]
        AND     R3, R3, R1
        EOR     R3, R3, R0
        STRB    R3, [R2, #LatchBSoftCopy]

        PLP
        Pull    "R2, R3, PC"

; The initial values for all of the osbyte variables
; as decreed by arthur.

ByteVarInitTable
                        ; The main osbyte variables, accessed
                        ; via calls &A6 to &FF

  DCW OsbyteVars-&A6    ; VarStart  #  2     ; &A6,&A7. Note that OS_Byte &A6/&A7 now returns hardcoded values instead of referring to this value held in workspace; potentially we could remove/reuse this entire 10 byte block in the future.
  = 0,0                 ; ROMPtr    #  2     ; &A8,&A9
  = 0,0                 ; ROMInfo   #  2     ; &AA,&AB
  = 0,0                 ; KBTran    #  2     ; &AC,&AD
  = 0,0                 ; VDUvars   #  2     ; &AE,&AF
                        ;
  = 0                   ; CFStime   #  1     ; &B0
  = 0                   ; InputStream #  1   ; &B1
  = &FF                 ; KeyBdSema #  1     ; &B2
                        ;
  = &00                 ; ROMPollSema # 1    ; &B3
  = &80                 ; OSHWM     #  1     ; &B4 (hi-byte of &8000)
                        ;
  = 1                   ; RS423mode #  1     ; &B5
  = 0                   ; NoIgnore  #  1     ; &B6
  = &00                 ; CFSRFS    #  1     ; &B7
  = &00,&00             ; VULAcopy  #  2     ; &B8,&B9
                        ;
  = &00                 ; ROMatBRK  #  1     ; &BA
  = &FF                 ; BASICROM  #  1     ; &BB
                        ;
  = &04                 ; ADCchanel #  1     ; &BC
  = &04                 ; ADCmaxchn #  1     ; &BD
  = &00                 ; ADCconv   #  1     ; &BE
                        ;
  = &FF                 ; RS432use     #  1  ; &BF
  = &42                 ; RS432conflag #  1  ; &C0
                        ;
  = &19                 ; FlashCount # 1     ; &C1
  = &19                 ; SpacPeriod # 1     ; &C2
  = &19                 ; MarkPeriod # 1     ; &C3
                        ;
  = &32                 ; KeyRepDelay # 1    ; &C4
  = &08                 ; KeyRepRate  # 1    ; &C5
                        ;
  = &00                 ; ExecFileH   # 1    ; &C6
  = &00                 ; SpoolFileH  # 1    ; &C7
                        ;
  = &00                 ; ESCBREAK    # 1    ; &C8 (200)
                        ;
  = &00                 ; KeyBdDisable # 1   ; &C9
  = &34                 ; KeyBdStatus  # 1   ; &CA
                        ;
  = &11                 ; RS423HandShake # 1 ; &CB
  = &00                 ; RS423InputSupr # 1 ; &CC
  = &00                 ; RS423CFSFlag   # 1 ; &CD
                        ;
  = &00                 ; EconetOScall # 1   ; &CE
  = &00                 ; EconetOSrdch # 1   ; &CF
  = &00                 ; EconetOSwrch # 1   ; &D0
                        ;
  = &00                 ; SpeechSupr # 1     ; &D1
  = &00                 ; SoundSupr # 1      ; &D2
                        ;
  = &01                 ; BELLchannel # 1    ; &D3
  = &90                 ; BELLinfo    # 1    ; &D4
  = &64                 ; BELLfreq    # 1    ; &D5
  = &06                 ; BELLdur     # 1    ; &D6
                        ;
  = &81                 ; StartMessSupr # 1  ; &D7
                        ;
  = &00                 ; SoftKeyLen # 1     ; &D8
                        ;
  = &00                 ; PageModeLineCount # 1          ; &D9
                        ;
  = &00                 ; VDUqueueItems # 1  ; &DA
                        ;
  = &09                 ; TABch # 1          ; &DB
  = &1B                 ; ESCch # 1          ; &DC
                        ;
  = &01,&D0,&E0,&F0     ; IPbufferCh # 4     ; &DD,&DE,&DF,&E0
  = &01,&80,&90,&00     ; RedKeyCh   # 4     ; &E1,&E2,&E3,&E4
                        ;
  = &00                 ; ESCaction  # 1     ; &E5
  = &00                 ; ESCeffect  # 1     ; &E6
                        ;
  = &00                 ; u6522IRQ # 1       ; &E7
  = &00                 ; s6850IRQ # 1       ; &E8
  = &00                 ; s6522IRQ # 1       ; &E9
                        ;
  = &00                 ; TubeFlag # 1       ; &EA
                        ;
  = &00                 ; SpeechFlag # 1     ; &EB
                        ;
  = &00                 ; WrchDest # 1       ; &EC
  = &00                 ; CurEdit  # 1       ; &ED
                        ;

  = &30                 ; KeyBase            ; &EE
  = &01                 ; Shadow             ; &EF
  = &00                 ; Country            ; &F0
                        ;
  = &00                 ; UserFlag # 1       ; &F1
                        ;
  = &64                 ; SerULAreg # 1      ; &F2
                        ;
  = &05                 ; TimerState # 1     ; &F3
                        ;
  = &FF                 ; SoftKeyConsist # 1 ; &F4
                        ;
  = &01                 ; PrinterDrivType   # 1          ; &F5
  = &0A                 ; PrinterIgnore     # 1          ; &F6
                        ;
  = &01,&00,&00         ; BREAKvector # 3    ; &F7,&F8,&F9
                        ;
  = &00                 ; DRIVER             ; &FA
  = &00                 ; DISPLAY            ; &FB
                        ;
  = &FF                 ; LangROM # 1        ; &FC
                        ;
  = &01                 ; LastBREAK # 1      ; &FD
                        ;
  = &0F                 ; KeyOpt # 1         ; &FE
                        ;
  = &08                 ; StartOptions # 1   ; &FF
                        ;
                        ;
ByteVarInitTableEnd

ByteVarInitTableSize * ByteVarInitTableEnd - ByteVarInitTable

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

        LTORG

oldirqowner & IRQ

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;
;       TranslateMonitorLeadType - Determine monitor type and default mode + sync from monitor lead type
;
; in:   Monitor lead type in variable MonitorLeadType (surprisingly!)
;
; out:  r3 = default mode to use
;       r4 = default monitortype to use
;       r5 = default sync to use
;

TranslateMonitorLeadType Entry "r0-r2"
      [ ZeroPage = 0
        MOV     r1, #Service_MonitorLeadTranslation
        LDRB    r2, [r1, #MonitorLeadType-Service_MonitorLeadTranslation]
      |
        LDR     r2, =ZeroPage
        MOV     r1, #Service_MonitorLeadTranslation
        LDRB    r2, [r2, #MonitorLeadType]
      ]
        SWI     XOS_ServiceCall
        TEQ     r1, #0                          ; if service claimed, then exit with these numbers
        EXIT    EQ

        ADR     r0, MonitorLeadList
10
        LDR     r14, [r0], #4
        EOR     r1, r2, r14, LSR #24            ; differences
        EOR     r14, r14, #&FF000000            ; make don't cares into zero
        TST     r14, #&C0000000
        BICEQ   r1, r1, #&C0                    ; knock out difference pairs if don't care
        TST     r14, #&30000000
        BICEQ   r1, r1, #&30
        TST     r14, #&0C000000
        BICEQ   r1, r1, #&0C
        TST     r14, #&03000000
        BICEQ   r1, r1, #&03
        TEQ     r1, #0                          ; if still have differences, then loop
        BNE     %BT10

        MOV     r0, #&FF
        AND     r3, r0, r14                     ; mode in bits 0..7
        AND     r4, r0, r14, LSR #8             ; monitortype in bits 8..15
        AND     r5, r0, r14, LSR #16            ; sync in bits 16..23

        ; Give the current GraphicsV driver a chance to specify a better mode
        ; than whatever we've picked here
        Push    "r4"
        MOV     r0, r3
        VDWS    r4
        LDR     r4, [r4, #CurrentGraphicsVDriver]
        MOV     r4, r4, LSL #24
        ORR     r4, r4, #GraphicsV_StartupMode
        BL      CallGraphicsV
        Pull    "r4"
        MOV     r3, r0
        EXIT

        MACRO
        MonitorLeadItem $lead, $mode, $monitortype, $sync
        ASSERT $lead < 256
        ASSERT $mode < 256
        ASSERT $monitortype < 256
        ASSERT $sync < 256
        DCD     (($lead):SHL:24):OR:(($sync):SHL:16):OR:(($monitortype):SHL:8):OR:($mode)
        MEND


MonitorLeadList
        ; KJB - changed default modes to 256 colours
        MonitorLeadItem 4_3330,  28, 3, 0                       ; VGA-capable monitors
        MonitorLeadItem 4_3111,  28, 5, 0                       ; Nothing - try LCD (fudge fudge)
        MonitorLeadItem 4_3333,  15, 0, 1                       ; Others - assume TV standard


; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;
;       ReadUniqueID - Read unique machine ID
;

ReadUniqueID
        Entry   "r0-r3,r9,r12"
        AddressHAL
        ; Check for extended ID first
        MOV     r0, #0
        CallHAL HAL_ExtMachineID
        CMP     r0, #0
        BEQ     %FT10
        MOV     r2, sp
        SUB     r0, r2, r0
        BIC     r0, r0, #3
        MOV     sp, r0
        Push    "r0,r2" ; Remember old SP, buffer pointer
        CallHAL HAL_ExtMachineID
        Pull    "r1"
        MOV     r2, #0
        MOV     r3, #0
05
        ; Construct the 7 byte machine ID using this simple algorithm:
        ; EOR each extended ID byte with the low byte of the 7 byte ID,
        ; then rotate left 7 bits
        LDRB    r12, [r1], #1
        EOR     r2, r2, r12
        AND     r12, r3, #&fe0000
        MOV     r3, r3, LSL #7
        ORR     r3, r3, r2, LSR #25
        MOV     r2, r2, LSL #7
        ORR     r2, r2, r12, LSR #17
        SUBS    r0, r0, #1
        BNE     %BT05
        LDR     sp, [sp] ; Restore SP
      [ ZeroPage <> 0
        LDR     r0, =ZeroPage
      ]
        STR     r2, [r0, #RawMachineID+0]
        STR     r3, [r0, #RawMachineID+4]
        ; Abuse CheckCRC to calculate CRC byte
        BL      CheckCRC
        LDR     r0, =ZeroPage
        STRB    r2, [r0, #RawMachineID+7]
        EXIT

10
        CallHAL HAL_MachineID
        LDR     r3, =ZeroPage
        STR     r0, [r3, #RawMachineID+0]
        STR     r1, [r3, #RawMachineID+4]
        BL      CheckCRC
        BVS     IDError
        EXIT

IDError
        DebugTX "Machine ID duff,zero substituted"
        MOV     r0, #0
      [ ZeroPage = 0
        STR     r0, [r0, #RawMachineID+0]       ; indicate no ID by putting zero here
        STR     r0, [r0, #RawMachineID+4]
      |
        LDR     lr, =ZeroPage
        STR     r0, [lr, #RawMachineID+0]       ; indicate no ID by putting zero here
        STR     r0, [lr, #RawMachineID+4]
      ]
        EXIT

CheckCRC ROUT
        ; Note: artificial ID generator relies on the required CRC being returned in R2!
        LDR     r1, =ZeroPage+RawMachineID      ; pointer to current byte
        MOV     r2, #0
        MOV     r3, #7                          ; number of bytes to do
10
        LDRB    r4, [r1], #1
        EOR     r2, r2, r4
        MOV     r4, #8                          ; number of bits to do
20
        MOVS    r2, r2, LSR #1                  ; shift bit out into carry
        EORCS   r2, r2, #&8C                    ; feedback carry into other bits
        SUBS    r4, r4, #1                      ; one less bit to do
        BNE     %BT20                           ; loop until done whole byte
        SUBS    r3, r3, #1                      ; one less byte to do
        BNE     %BT10                           ; loop until done all 7 bytes
        LDRB    r4, [r1], #1                    ; read CRC
        CMP     r4, r2                          ; if correct
        MOVEQ   pc, lr                          ; exit (V clear)
        RETURNVS                                ; else exit indicating error

        LTORG

        END