; 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.
;
        SUBT    => NewReset

DuffEntry *     31*1024*1024+32*1024    ; Never any memory at this address

SoftReset       * 0                     ; Reset types
PowerOnReset    * 1
ControlReset    * 2

; CMOS RAM resetting stuff:
CMOSLimit       * &F0

; Keyboard flags
                ^ 1
CTRL_Down_Flag  # 1
SHIFT_Down_Flag # 1
KB_There_Flag   # 1

R_Down_Flag     # 1      ; note that these 4 form one word!!
T_Down_Flag     # 1
Del_Down_Flag   # 1
Copy_Down_Flag  # 1

KeyDataPtr      # 4
 [ MorrisSupport
  [ STB
Port2Present    # 4
  |
Port2Present    # 1
Port3Present    # 1
UnusedByte3     # 1
UnusedByte4     # 1
  ]
 ]

 [ MEMM_Type = "ARM600"

; On ARM600, InitKbdWs is in zero page - check it's big enough

        ASSERT  @ <= ?InitKbdWs
 |

; Else use PhysRam start

InitKbdWs       *       PhysRam
 ]

 [ MEMM_Type <> "ARM600"
;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; routine to set up a 32K chunk (for fixed areas)
; Enter with R8 = PageSize, R2 = physram offset, R3 logaddr
; R8 preserved on exit

; This routine is not needed on ARM600, as MemSize sets up the L2PT for the static pages

SlapIn32K
        MOV     R12, lr
        ADRL    R1, PageShifts-1
        LDRB    R1, [R1, R8, LSR #12]
        MOV     R2, R2, LSR R1          ; DivRem  R0, R2, R8, R1
                                        ; R2 := cam entry no
        ADD     R5, R3, #32*1024
SlapInNext
        BL      BangCam
        ADD     R2, R2, #1
        ADD     R3, R3, R8
        CMP     R3, R5
        BNE     SlapInNext
        MOV     PC, R12
 ]


;       AddCamEntries
;
; in:   r0 -> appropriate part of CAM map
;       r1 = ap (+ CB bits in new world)
;       r2 = log address
;       r7 = amount of cam map to do
;       r8 = PageSize
;
; out:  r0, r3-r12 preserved
;       r1 corrupted
;       r2 updated by relevant amount

AddCamEntries ROUT
 [ ExpandedCamMap
        Push    "r0, lr"
        MOV     lr, r1                  ; access privs (PPL)
        MOV     r1, r7
01
        STMIA   r0!, {r2, lr}           ; store logaddr, PPL
        ADD     r2, r2, r8              ; increment address by 1 page
        SUBS    r1, r1, #8              ; decrement count of how much to do
        BNE     %BT01
        Pull    "r0, pc"
 |
        MOV     R1, #0
        CMP     r2, #CursorChunkAddress
        ORREQ   r2, r2, #&10000000      ; appropriate PPL
01
        STR     R2, [R0, R1]
        ADD     R1, R1, #4
        ADD     R2, R2, R8
        CMP     R1, R7
        BNE     %BT01
        MOVS    pc, lr
 ]

; GetConfiguredSize - convert CMOS address into amount of memory
; in:   r0 = CMOS address
; out:  r0 corrupted
;       r2 = amount in bytes

; NB this routine doesn't do screen size mangling (yet!)

GetConfiguredSize ENTRY "r1"
        MOV     r1, #0
        LDR     r1, [r1, #Page_Size]
        ADRL    lr, PageShifts-1
        LDRB    r1, [lr, r1, LSR #12]   ; r1 = log2 pagesize
        MOV     r2, #127                ; mask of value

        TEQ     r0, #FontCMOS           ; if fontsize
        MOVEQ   r1, #12                 ; then in units of 4K, not pagesize
        MOVEQ   r2, #255                ; and use full byte

        BL      Read                    ; read CMOS ram
        AND     r2, r0, r2              ; value anded with mask
        MOV     r2, r2, LSL r1          ; and shifted up accordingly
        EXIT

FudgeConfigureRMA
        Push   lr
        B      ConfigureRMA

ReadCMOSAndConfigure ROUT
; R0 = index into CMOS RAM of byte with size in
; R1 = place to save amount moved
; R2 = CAM entry number to start at: updated
; R3 = LogRam Address to move memory to
; r11  PPL
; Check for memory left, move as much as poss
        Push    lr
        BL      Read                    ; CMOS byte -> R0

 [ :LNOT: NewStyle_FontArea
        CMP     r3, #FontCacheAddress
        MOVEQ   r0, r0, LSL #12         ; *4K
        BEQ     NotScreen
 ]

        AND     R0, R0, #127            ; mask to same bitfield as status
ConfigureRMA
        MOV     R10, #0
        LDR     R10, [R10, #Page_Size]
        MUL     R0, R10, R0             ; get size in bytes
        CMP     R3, #ScreenEndAdr       ; screen?
        BNE     NotScreen

; quick pokery for sensible screensize

        BL      MassageScreenSize
        SUB     R3, R3, R0              ; step back.
NotScreen
        MOV     R5, #0                  ; amount moved
        CMP     R0, #0
        BEQ     NoMoreMemory

 [ GetPagesFromFreePool

; r0 = amount of memory to move
; r1 = address to store size in
; (r2 = page number to start at, ignored in our method)
; r3 = address of where to put memory
; r10 = page size
; r11 = ap + CB

        MOV     r6, r11                         ; r6 = ap + CB
        MOV     r4, #FreePoolDANode
        LDR     r7, [r4, #DANode_Base]
        LDR     r8, [r4, #DANode_Size]
        ADD     r7, r7, r8                      ; r7 -> end of free pool +1
10
        CMP     r8, r10                         ; if no free memory left
        BCC     %FT20                           ; then tidy up
        SUB     r7, r7, r10                     ; move free pool pointer backwards
        Push    "r0, r1"
        MOV     r0, r7
        MOV     r1, r3
        BL      MovePageAtR0ToR1WithAccessR6
        Pull    "r0, r1"
        ADD     r3, r3, r10                     ; advance "to" pointer
        SUB     r8, r8, r10                     ; one less page of free memory
        ADD     r5, r5, r10                     ; one more page done
        SUBS    r0, r0, r10
        BNE     %BT10
20
        STR     r8, [r4, #DANode_Size]
 |
        LDR     R8, [R5, #RAMLIMIT]

; now set R6 = first entry not to use
;         R7 = end of gap   "   "  "
;         R8 = last entry we can use

  [ MEMM_Type = "ARM600"
        MOV     r7, #0
        LDR     r6, [r7, #VideoSize]            ; find out how many pages in video area
        MOV     r6, r6, LSR #12                 ; = page number of start of skipped bit
        ASSERT  SoftCamMapSize = L2PTSize +4
        MOV     r7, #L2PTSize
        LDMIA   r7, {r7, lr}                    ; r7 = L2PTSize, lr = SoftCamMapSize
        ADD     r7, r7, lr                      ; add sizes together
        ADD     r7, r7, #StaticPagesSize + UndStackSize ; + number of bytes used for other static bits
        ADD     r7, r6, r7, LSR #12             ; r7 = page number after static bit
        MOV     r8, r8, LSR #12                 ; make r8 into highest page number+1
  |
        CMP     R8, #512*1024
        MOVEQ   R6, #(512-96)*1024
        MOVEQ   R7, #512*1024
        MOVNE   R6, #(512-32)*1024
   [ MEMM_Type = "MEMC2"
        MOVNE   R7, #(512+96)*1024      ; if MEMC2, skip L2PT as well
   |
        MOVNE   R7, #(512+64)*1024
   ]
        Push    "R2"

        ADRL    R2, PageShifts-1
        LDRB    R2, [R2, R10, LSR #12]
        MOV     R6, R6, LSR R2          ; DivRem  R6, R6, R10, R2
        MOV     R7, R7, LSR R2
        MOV     R8, R8, LSR R2

        Pull    "R2"
  ]

CAMZapLoop
        CMP     R2, R6                  ; if at gap, skip
        MOVEQ   R2, R7
        CMP     R2, R8                  ; if no more memory, give in
        BEQ     NoMoreMemory
        ADD     R5, R5, R10
        Push    "R0, R1, R6"
        BL      BangCamUpdate
        Pull    "R0, R1, R6"
        ADD     R2, R2, #1
        ADD     R3, R3, R10
        SUBS    R0, R0, R10
        BGT     CAMZapLoop
 ]
NoMoreMemory
        STR     R5, [R1]
        Pull    "PC"

; MassageScreenSize - called from ReadCMOSAndConfigure (above) and also from
; ReadSysInfo

MassageScreenSize ROUT
        CMP     r0, #0
        BNE     CmosScreenWillDo
        LDR     r0, [r0, #RAMLIMIT]
        CMP     r0, #512*1024
        MOVEQ   r0, #80*1024
        MOVNE   r0, #160*1024
CmosScreenWillDo
        CMP     r0, #20*1024            ; ensure mode 0 gettable
        ADDCC   r0, r0, r10             ; if not, then add another page
        BCC     CmosScreenWillDo
        CMP     r0, #ScreenMaxSize
        MOVHI   r0, #ScreenMaxSize
        MOV     pc, lr

        LTORG

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; Data tables: VIDC := mode 0, all palette black

VIDCTAB
 [ VIDC_Type = "VIDC20"

; Program Control Register first, to clear power-down bit
; Now depending upon the VIDCClockSource flag, re-program the clock source.
   [ VIDCClockSource = "VCO"
     [ VCOstartfix
        & &E0000404     ; CR: FIFO load 16 words, 1 bpp, ck/2, vclk (allow for doubled VCO freq)
     |
        & &E0000400     ; CR: FIFO load 16 words, 1 bpp, ck/1, vclk
     ]
   ]
   [ VIDCClockSource = "HCLK"
        & &E0000401     ; CR: FIFO load 16 words, 1 bpp, ck/1, hclk
   ]
   [ VIDCClockSource = "RCLK"
        & &E0000406     ; CR: FIFO load 16 words, 1 bpp, ck/2, rclk
   ]

; Don't bother programming all 256 palette entries, we'll be here all night
; Since we're setting up a 1 bit-per-pixel mode, just do colours 0 and 1

        & &10000000     ; Palette address register = 0
        & &00000000     ; Colour 0 = black
        & &00000000     ; Colour 1 = black
        & &40000000     ; Border colour = black
        & &50000000     ; Pointer colour 1 = black
        & &60000000     ; Pointer colour 2 = black
        & &70000000     ; Pointer colour 3 = black

; Get a stable display up so we get stable signals

        & &800003F8     ; HCR  = 76 + 88 + 96 + 640 + 96 + 28
        & &81000044     ; HSWR = 76
        & &82000098     ; HBSR = 76 + 88
        & &830000F2     ; HDSR = 76 + 88 + 96
        & &84000372     ; HDER = 76 + 88 + 96 + 640
        & &850003D8     ; HBER = 76 + 88 + 96 + 640 + 96
        & &860000F3     ; HCSR = HDSR

        & &90000137     ; VCR  = 3 + 19 + 16 + 256 + 16 + 2
        & &91000002     ; VSWR = 3
        & &92000015     ; VBSR = 3 + 19
        & &93000025     ; VDSR = 3 + 19 + 16
 [ MEMC_Type = "IOMD"
  [ Simulator
        & &94000025     ; No displayed lines, so we don't get video FIFO u/flow when simulating
  | ;Simulator
        & &94000125     ; VDER = 3 + 19 + 16 + 256
  ] ;Simulator
 | ;MEMC_Type <> "IOMD"
        & &94000125     ; VDER = 3 + 19 + 16 + 256
 ] ;MEMC_Tupe = "IOMD"
        & &95000135     ; VBER = 3 + 19 + 16 + 256 + 16
        & &96000025     ; VCSR = VDSR
        & &97000025     ; VCER = VDSR

        & &B1000001     ; SCR: sound disabled (+use 24MHz clock)

        & &C00F1003     ; EREG = comp sync, DACs on, ereg output ext lut
   [ VCOstartfix
        & &D0000302     ; FSYNREG, clk = (3+1)/(2+1) * 24MHz = 32MHz  (higher frequency as part of fix)
   |
        & &D0000305     ; FSYNREG, clk = (3+1)/(5+1) * 24MHz = 16MHz
   ]
        & &F0013000     ; DCR: bus D[31:0], Hdisc       ;RCM 29/9/94: changed from &F0012000 at PSwindells request
        & &FFFFFFFF     ; That's the lot
 | ;VIDC_Type<>"VIDC20"
        & &00000000
        & &04000000
        & &08000000
        & &0C000000
        & &10000000
        & &14000000
        & &18000000
        & &1C000000
        & &20000000
        & &24000000
        & &28000000
        & &2C000000
        & &30000000
        & &34000000
        & &38000000
        & &3C000000
        & &40000000
        & &44000000     ; Cursor -> black
        & &48000000
        & &4C000000     ; Palette programmed (avoid messy screen on reset)

        & &807FC000     ; HCR  Get a stable display up so we get stable signals
        & &8408C000     ; HSWR
        & &881B0000     ; HBSR
        & &94770000     ; HBER
        & &A04E0000     ; VCR
        & &A4024000     ; VSWR
        & &A8050000     ; VBSR
        & &B44E0000     ; VBER
        & &C0000100     ; SFR  NB. TEST BIT !!! - also DFlynn requested value
        & &E00000B2     ; CR   Set 640*256, 1 bit per pixel, rate of 12MHz
                        ; change bottom byte to 22 for Linear Microvitecs (CS)
                        ;                       B2 for Monochrome/Philips (SS)
        & &8C208000     ; HDSR
        & &90708000     ; HDER
        & &98258000     ; HCSR
        & &9C400000     ; HIR
        & &AC098000     ; VDSR
        & &B0498000     ; VDER
        & &B8098000     ; VCSR
        & &BC498000     ; VCER
; don't mess with the stereo image registers: sound code will set them.
        & &FFFFFFFF     ; That's the lot
 ] ;VIDC_Type = "VIDC20"


 [ MEMM_Type <> "ARM600"
        GBLL    ARM3_Support
 ]

 [ :DEF:ARM3_Support
ARM3            CP     15

CR0             CN      0
CR_ID           CN      0
CR_Flush        CN      1
CR_Control      CN      2
CR_Cacheable    CN      3
CR_Updateable   CN      4
CR_Disruptive   CN      5

UndefVec *      &00000004

; Default cacheable areas
;
; ROM                   Cacheable       (&03400000..&03FFFFFF)
; I/O                   Not cacheable   (&03000000..&033FFFFF)
; Phys RAM              Not cacheable   (&02000000..&02FFFFFF)
; Log RAM (screen)      Not cacheable   (&01E00000..&01FFFFFF)
; Log RAM (RAM Disc)    Not cacheable   (&01000000..&013FFFFF)
;                       (No point in caching RAM Disc)
; Log RAM (non-screen)  Cacheable       (&00000000..&01DFFFFF)
Cacheable_Default DCD     2_11111100000000000111110011111111

; Default updateable areas
;
; ROM/CAM               Not updateable  (&03800000..&03FFFFFF)
; ROM/DAG               Not updateable  (&03400000..&037FFFFF)
; I/O                   Not updateable  (&03000000..&033FFFFF)
; Phys RAM              Not updateable  (&02000000..&02FFFFFF)
; Log RAM (screen)      Not updateable  (&01E00000..&01FFFFFF)
; Log RAM               Updateable      (&00000000..&01DFFFFF)
Updateable_Default DCD    2_00000000000000000111111111111111

; Default disruptive areas
;
; CAM                   Disruptive      (&03800000..&03FFFFFF)
; DAG                   Not disruptive  (&03400000..&037FFFFF)
; I/O                   Not disruptive  (&03000000..&033FFFFF)
; Phys RAM              Not disruptive  (&02000000..&02FFFFFF)
; Log RAM               Not disruptive  (&00000000..&01FFFFFF)
Disruptive_Default *    2_11110000000000000000000000000000

UndefRet
        MOVS    pc, lr
 ]

VIDCPhys        *       &03400000       ; used to address VIDC when MMU is off

; Entered here after RESET (or BREAK)

; This code must be capable of being executed at a non-ROM address and with the MMU off,
; and running in 32-bit mode up until the call to MemSize.

CONT_Break
        MOV     r1, #1          ; parameter passed to InitMEMC, to indicate Break not Reset
        B       Continue

CONT    ROUT
        MOV     r1, #0          ; parameter passed to InitMEMC, to indicate Reset not Break
Continue

; First bang MEMC to ensure safety

        BL      InitMEMC        ; initialise MEMC CR, and turn off MMU if it's on

; VInit etc set on ze mode change: no DMA going yet so don't set owt.

        MOV     R1, #VIDCPhys   ; Must ALWAYS initialise VIDC on reset or else
        ADR     R2, VIDCTAB     ; we may get vsync interrupts that stiff us
10      LDR     R0, [R2], #4    ; permanently as VIDC is in an undefined state
        CMP     R0, #-1         ; so have mode 0 with all black palette
        STRNE   R0, [R1]
        BNE     %BT10

; Now bang IOC (disable all but keyboard interrupts)

        MOV     R1, #IOC
        MOV     R0, #&FF                ; all inputs
        STRB    R0, [R1, #IOCControl]   ; in case called by Tim

        MOV     R0, #0
        STRB    R0, [R1, #IOCIRQMSKA]   ; kein interrupts
        STRB    R0, [R1, #IOCFIQMSK]    ; disable FIQs
 [ IO_Type = "IOMD"
        STRB    R0, [R1, #IOMD_DMAMSK]  ; disable DMA interrupts, too
 ]

 [ Keyboard_Type = "PC" :LAND: IO_Type <> "IOMD"
        MOV     R0, #podule_IRQ_bit     ; used for PC keyboard h/w on a podule
 |
        MOV     R0, #KARTRxBit          ; used for Archi keyboard or IOMD PC keyboard
 ]
        STRB    R0, [R1, #IOCIRQMSKB]   ; allow communication with kbd, when I_bit gets cleared

; now bits to allow CMOS read/write : need timer

        LDR     R0, =20000-1    ; R0 = Timer delay (units of 0.5 microsecond)
                                ; 20000*0.5E-6 = 0.01 Seconds (100Hz ticker)
                                ; TMD 21-May-93: "-1" correction applied

        STRB    R0, [R1, #Timer0LL]     ; Set up the delay
        MOV     R0, R0, LSR #8
        STRB    R0, [R1, #Timer0LH]
        STRB    R0, [R1, #Timer0GO]     ; and start the ticks

        MOV     R0, #timer0_bit
        STRB    R0, [R1, #IOCIRQCLRA]   ; Clear pending t0 interrupt j.i.c.

  [ VCOstartfix
        ;2nd part of fix for VCO failing to start on A7000 (esp. 7500FE) - forcing PCOMP high for about 3 ms
        LDRB    R0, [R1,#IOMD_ID0]
        CMP     R0, #&E7
        LDREQB  R0, [R1,#IOMD_ID1]
        CMPEQ   R0, #&D4
        BEQ     vcofix_notMorris      ; risky to force PCOMP on Risc PC
        MOV     R0, #VIDCPhys
        LDR     R2, =&D0000342        ; VIDC20 FSYNREG, as in VIDCTAB but with force PCOMP high
        STR     R2, [R0]
        MOV     R0, #3072*2           ; time delay of about 3 ms (0.5 us units)
        STRB    R0, [R1, #Timer0LR]   ; copy counter into output latch
        LDRB    R2, [R1, #Timer0CL]   ; R2 := low output latch
vcofix_waitloop
        STRB    R0, [R1, #Timer0LR]   ; copy counter into output latch
        LDRB    R3, [R1, #Timer0CL]   ; R3 := low output latch
        TEQ     R3, R2                ; unchanged ?
        BEQ     vcofix_waitloop       ; then loop
        MOV     R2, R3                ; copy anyway
        SUBS    R0, R0, #1            ; decrement count
        BNE     vcofix_waitloop       ; loop if not finished
        MOV     R0, #VIDCPhys
        LDR     R2, =&D0000302        ; VIDC20 FSYNREG, as in VIDCTAB (PCOMP low again)
        STR     R2, [R0]
vcofix_notMorris
  ]

; now size memory

        BL      MemSize                 ; out: r0 = page size, r1 = memory size, r2 = MEMC CR value, r3-r14 corrupt

        MOV     R8, R0                  ; R8 = page size in bytes
        MOV     R13, R1                 ; R13 is now the RAM size
        MOV     R9, R2                  ; need this to set soft copy right

   [ RunningOnEmul :LAND: :LNOT: STB
        MOV     R7,#&80
        ORR     R7,R7,#&3E00            ; r7 := &3E80 = 16000 (standard Risc PC value)
   |
        BL      TimeCPU                 ; r7 := CPU speed in kHz/MEMC1a flag
   ]

; first ensure nothing dangerous in MEMC

 [ MEMM_Type <> "ARM600"                ; all this stuff is done in MemSize
 [ {TRUE}                               ; new code which allows for MEMC2's small pages
        ADRL    r2, PageShifts-1
        LDRB    r2, [r2, r8, LSR #12]   ; get log2(pagesize)
        MOV     R2, R13, LSR R2         ; number of pages=total memory / pagesize
        CMP     R2, #128                ; but if fewer than 128 (eg 64 on A305)
        MOVCC   R2, #128                ; then use 128 (all MEMC1's pages need initialising,
                                        ; even if half of them are not in use)
 |
        CMP     R13, #8*1024*1024       ; if >= 8MBytes,
        MOVCS   R2, R13, LSR #15        ; then there are R13/32K pages
        MOVCC   R2, #128                ; else there are 128 pages
 ]
        SUB     R2, R2, #1              ; maximum page number

        LDR     R3, =DuffEntry          ; a non-existant piece of memory
        MOV     R11, #AP_Duff           ; go away, users
EmptyCamEntries
        BL      BangCam                 ; ensure not mapped to anything
        SUBS    R2, R2, #1              ; dangerous
        BPL     EmptyCamEntries

; Put in position all fixed areas of memory
;   32K for cursor etc     : CAM entries for 512-32 to 512K in PhysRam

; SlapIn32K needs R8 = pagesize
; This is now already set up, so no need to compute it from
; MEMC CR (which would be wrong on ARM600 anyway)

        MOV     R11, #1                 ; user write protected
        MOV     R3, #CursorChunkAddress
        MOV     R2, #(512-32)*1024
        BL      SlapIn32K

        MOV     R3, #SysHeapChunkAddress ; 32K forced in system heap area
; use phys 512+32 to 512+64K if R13<>512K,  512-96 to 512-64K if R13=512K
        CMP     R13, #512*1024
        MOVEQ   R2, #(512-96)*1024
        MOVNE   R2, #(512+32)*1024

        MOV     r11, #0
        BL      SlapIn32K

        MOV     R3, #0                  ; 32K system work area

; use phys 512 to 512+32K if R13<>512K,  512-64 to 512-32K if R13=512K

        CMP     R13, #512*1024
        MOVEQ   R2, #(512-64)*1024
        MOVNE   R2, #512*1024

        BL      SlapIn32K
 ]

 [ :DEF:ARM3_Support
        MOV     r0, #UndefVec
        LDR     r1, [r0]
        LDR     r2, UndefRet
        MOV     r5, #0
        STR     r2, [r0]
        MRC     ARM3, 0, r5, CR_ID, CR0
        STR     r1, [r0]
        AND     r5, r5, #&ff00
        TEQ     r5, #&0300
        BNE     noarm3
        LDR     r0, Cacheable_Default
        LDR     r1, Updateable_Default
        MOV     r2, #Disruptive_Default
        MCR     ARM3, 0, r0, CR_Cacheable, CR0
        MCR     ARM3, 0, r1, CR_Updateable, CR0
        MCR     ARM3, 0, r2, CR_Disruptive, CR0
        MRC     ARM3, 0, r3, CR_Cacheable, CR0
        MRC     ARM3, 0, r4, CR_Updateable, CR0
        MRC     ARM3, 0, r5, CR_Disruptive, CR0
        TEQ     r0, r3
        TEQEQ   r1, r4
        TEQEQ   r2, r5
        BNE     noarm3
        MOV     r0, #3
        MCR     ARM3, 0, r0, CR_Control, CR0
noarm3
 ]

; the fixed areas give us : IRQ stack (in cursor), SVC stack (in sysheap base)
; and bottom block makes most sense done here

; now keyboard initialisation: initialise baud rate, send dummy, read dummy

        MOV     R0, #InitKbdWs
        MOV     R1, #0
        STRB    R1, [R0, #CTRL_Down_Flag] ; clear CTRL down flag, R down flag
        STRB    R1, [R0, #SHIFT_Down_Flag]
        STRB    R1, [R0, #KB_There_Flag]
        STR     R1, [R0, #R_Down_Flag]  ; all CMOS reset flags
        STR     R1, [R0, #KeyDataPtr]
 [ MorrisSupport
  [ STB
        STR     R1, [R0, #Port2Present]
  |
        STRB    R1, [R0, #Port2Present]
        STRB    R1, [R0, #Port3Present]
  ]
 ]
        B       SetUpKbd                ; No stack yet so branch and branch back.
SetUpKbdReturn


; set up reset interrupt handler (reads, discards keys, setting flags if CTRL or R)
; NB on ARM600 we need to go into 32-bit mode, so we can safely overwrite vectors

 [ CPU_Type = "ARM600"
        mrs     AL, r0, CPSR_all        ; switch into IRQ32, still IRQs disabled
        BIC     r0, r0, #&1F
        ORR     r1, r0, #IRQ32_mode
        msr     AL, CPSR_all, r1

        LDR     sp_irq, =IRQSTK         ; set up sp_irq

        ADRL    R2, MOSROMVecs          ; pick up from table
        LDR     R2, [R2, #&18]          ; this gets overwritten while active,
        MOV     R3, #0
        STR     R2, [R3, #&18]          ; but hopefully by the same value!

        ORR     r0, r0, #SVC32_mode     ; switch into SVC32

 [ StrongARM
   ;for StrongARM, we need to do an IMB type thing for modifying code in vector area
        ARM_read_ID r1
        AND     r1,r1,#&F000
        CMP     r1,#&A000
        BNE     vectorpoke_notSA_1
        MOV     r1,#0                   ;we clean one cache entry, 0..1F, = vector area
  [ SAcleanflushbroken
        ARMA_clean_DCentry r1
        ARMA_flush_DCentry r1
  |
        ARMA_cleanflush_DCentry r1
  ]
        ARMA_drain_WB
        ARMA_flush_IC
vectorpoke_notSA_1
    [ ARM810support
        ARM8_branchpredict_flush  ;IMB on ARM810, and harmless on other ARMs
    ]
 ]

        BIC     r0, r0, #I32_bit        ; and enable IRQs
        msr     AL, CPSR_all, r0

 ; in SVC32 from now until we've finished poking around with vectors

 |
        TEQP    pc, #IRQ_mode + I_bit   ; set up IRQ stack
        NOP
        LDR     sp_irq, =IRQSTK

        ADRL    R0, MOSROMVecs          ; pick up from table
        LDR     R0, [R0, #&18]          ; this gets overwritten while active,
        MOV     R1, #0                  ; could have been changed by keyboard initialisation
        STR     R0, [R1, #&18]          ; but hopefully by the same value!

        TEQP    pc, #SVC_mode           ; Enable interrupts, back to supervisor mode
        NOP
 ]


 [ :LNOT: AlwaysClearRAM

; IF por OR FX200 bit set THEN clear memory
        MOV     R0, #IOC
        LDRB    R1, [R0, #IOCIRQSTAA]
        ANDS    R1, R1, #por_bit
        BNE     %FT20

        LDR     R0, =OsbyteVars + :INDEX: ESCBREAK
        LDRB    R1, [R0]
        CMP     R1, #2                  ; unlike popular rumour, bit 1 ain't
        CMPNE   R1, #3                  ; a flag
        BNE     %FT30
20
 ]
        BL      ClearPhysRAM
30
        MOV     r0, #0
        STR     r9, [r0, #MEMC_CR_SoftCopy] ; set soft copy.
        STR     r7, [r0, #MemorySpeed]  ; Remember CPU speed/MEMC1a flag
        STR     r8, [r0, #Page_Size]    ; r8 is still page size from way up there
        STR     r13, [r0, #RAMLIMIT]    ; save sussed memory size in LogRam

        LDR     sp, =SVCSTK             ; set up a stack

; do as much other initialisation as possible, to give keyboard stuff time
 [ StrongARM
        BL      Processor_Type          ; Determines the processor type & stores it in page 0.
 ]

;StrongARM: OK, there is quite a bit of code poking below, to various addresses. We'll
;           defer IMB consideration till the poking's done, then do a full IMB (full
;           data cache clean). This avoids various little IMB's and removes chance of leaving
;           some unnoticed poked code in data cache. The deferral should be safe, because none
;           of the poked code will be used yet awhile (given that current IRQ hardware vector is
;           not actually changed below).

 [ ProcessorVectors
; Copy default processor vector table and default preveneers.
; Code relies on preveneers being immediately after processor vector table
; but could easily be changed into 2 copy loops.
        ASSERT  ProcVecPreVeneers = ProcVec_End
        ASSERT  DefaultPreVeneers = DefaultProcVecs+ProcVec_End-ProcVec_Start

        ADRL    R0, DefaultProcVecs
        LDR     R1, =ProcVec_Start
        MOV     R2, #ProcVec_End-ProcVec_Start+ProcVecPreVeneersSize
39
        LDR     R3, [R0], #4
        STR     R3, [R1], #4
        SUBS    R2, R2, #4
        BNE     %BT39
 ]

; copy handler addresses
        ADRL    R1, MOSROMVecs+4        ; don't copy to 0: key stuff is using
                                        ; it as workspace!
        MOV     R0, #4

40
        LDR     R2, [R1], #4            ; N.B. IRQ handler better be same as the one in there
        STR     R2, [R0], #4
        TEQ     R0, #EndFiq-MOSROMVecs
        BNE     %BT40

 [ CPU_Type = "ARM600"

; Now we have set up the hardware vectors we can drop back to SVC26 mode

        mrs     AL, r0, CPSR_all
        BIC     r0, r0, #&1F
        ORR     r0, r0, #SVC26_mode
        msr     AL, CPSR_all, r0
 ]


; Ensure any CMOS operation aborted

        MOV     R1,#16                          ; Two bytes in case RTC transmitting
35
        BL      Start                           ; Start/clock edge
        BL      Stop
        SUBS    R1,R1,#1
        BNE     %BT35

 [ CacheCMOSRAM
        BL      InitCMOSCache           ; initialise cache of CMOS RAM
 ]

; Now copy the initialised data
        MOV     R0, #IRQ1V

; first save IOC soft copy so can restore it

        LDRB    R2, [R0, #IOCControlSoftCopy-IRQ1V]
        Push    "R2"
        LDRB    R2, [R0, #CannotReset-IRQ1V]
        Push    "R2"

        ADRL    r1, StartData
DatCopy
        LDR     R2, [R1], #4
        STR     R2, [R0], #4
        TEQ     R0, #(EndData-StartData+IRQ1V)
        BNE     DatCopy

 [ ResetIndirected
        ADR     r2, CONT_Break
        MOV     r0, #0
        STR     r2, [r0, #ResetIndirection]
 ]

 [ CPU_Type = "ARM600"
        MOV     r0, #0                  ; initialise abort list
        STR     r0, [r0, #AbortIndirection]
 ]

; Now the SWI despatch + low part of SWI table
        ADRL    R3, DirtyBranch
        LDR     R0, =SWIDespatch
SVCTabCopy                              ; so copy the table
        LDR     R2, [R1], #4
        STR     R2, [R0], #4
        TEQ     R1, R3
        BNE     SVCTabCopy

; pad to 1K table here, rather than use ROM space
        ADRL    R2, NoSuchSWI
        LDR     R4, =1024+SvcTable      ; end of table
PadSVCTable
        CMP     R0, R4
        STRNE   R2, [R0], #4
        BNE     PadSVCTable

; now the dirty branch
        [ {TRUE}          ; first version is from STB, second from 3.70
        LDR     R1, [R1]  ; since R0, R1, R2 all get trashed soon stay
        STR     R1, [R0]  ; with the more efficient STB version
        |
        LDR     R2, [R1], #4
        STR     R2, [R0], #4
        ]

; now the time/date conversions

        LDR     R0, =SvcTable
        ADRL    R1, ConvertStandardDateAndTime
        STR     R1, [R0, #OS_ConvertStandardDateAndTime*4]
        ADD     R1, R1, #ConvertDateAndTime - ConvertStandardDateAndTime ; SKS
        STR     R1, [R0, #OS_ConvertDateAndTime*4]

; other conversion SWIs, all go through same entry point

        ADRL    R1, despatchConvert
        MOV     R2, #OS_ConvertHex1
conversionSWIfill
        STR     R1, [R0, R2, LSL #2]
        ADD     R2, R2, #1
        CMP     R2, #OS_ConvertFileSize+1
        BNE     conversionSWIfill

;StrongARM: OK, that completes the poking around, some of which is code. Now let's
;           do a full IMB type thing, to be safe (if we're running on StrongARM)
  [ StrongARM
        ARM_read_ID R0
        AND     R0,R0,#&F000
        CMP     R0,#&A000
        BNE     afterpokingaround_notSA
        MOV     R1,#0
        STRB    R1,[R1,#SyncCodeA_sema]    ;initialise semaphore to 0
        MOV     R1,#ARMA_Cleaner_flipflop
        LDR     R0,=ARMA_Cleaners_address  ;note: we are initialising ARMA_Cleaner_flipflop here
        STR     R0,[R1]
        ARMA_clean_DC R0,R1,R2
        ARMA_drain_WB
        ARMA_flush_IC
afterpokingaround_notSA
    [ ARM810support
        ARM8_branchpredict_flush  ;IMB on ARM810, and harmless on other ARMs
    ]
  ]

; Initialise CAO ptr to none.

        MOV     R0, #0
        MOV     R1, #32*1024*1024       ; nothing will be here!!
        STR     R1, [R0, #Curr_Active_Object]

    [ RunningOnEmul :LAND: :LNOT: STB
KeyWait *       1
    |
KeyWait * 200000                 ; 1/5 sec wait (in microseconds)
    ]

    [ KeyWait <> 0
; Check for keyboard there every 1/5 sec. but give up after 2 secs.
        MOV     r2, #IOC
        MOV     r3, #10                 ; Check for keyboard 10 times (2 secs max).
        MOV     r4, #InitKbdWs
kbdwait
        LDRB    r5, [r4, #KB_There_Flag]
        LDR     r0, =KeyWait*2          ; Wait 1/5 second (give keys down a chance to come in).
        BL      DoMicroDelay
        TEQ     r5, #0                  ; If keyboard was there 1/5 second ago then
        BNE     kbdthere                ;   continue reset
        SUBS    r3, r3, #1              ; else wait a maximum of 2 seconds.
        BNE     kbdwait
kbdthere
    ]


 [ ValidateCMOS :LAND: STB
; Do a POR if some super-critical values are shagged or if checksum is invalid.
        MOV	R3, #-1                 ; do all RAM if we do any
        BL      ValChecksum             ; Always check the checksum
        BNE	cmos_reset

; ScreenSizeCMOS, RAMDiscCMOS, SysHeapCMOS, RMASizeCMOS and SpriteSizeCMOS
; should be 0. Happily they are at consecutive addresses so we can loop through
; them.
	MOV	R1, #ScreenSizeCMOS
reset_loop
	MOV	R0, R1
	BL	Read
	TEQ	R0, #0
	BNE	cmos_reset
	INC	R1
	TEQ	R1, #SpriteSizeCMOS
	BHI	reset_loop

 [ {FALSE}
; FontCMOS should be <= 32 (128K)
; What on earth was this about? Why would anyone require FontSize to
; be less than 128K?
	MOV	R0, #FontCMOS
	BL	Read
	CMP	R0, #32
	BHI	cmos_reset
 ]

 [ {FALSE}
 ; Oh, just leave it be
	MOV	R0, #VduCMOS
	BL	Read
 [ IOMD_C_MonitorType = 0 :LAND: IOMD_C_PALNTSCType = 0
; Force TV if we don't have a MonitorType auto-detect bit
	TEQ	R0, #(Sync_Separate :OR: MonitorType0)
 |
; Force auto-detect of monitor stuff if we have a MonitorType auto-detect bit
	TEQ	R0, #(Sync_Auto :OR: MonitorTypeAuto)
 ]
	BNE	cmos_reset
 ]

; Year should be >=1995, <=2020
; (2020 is arbitrary, but everything breaks soon after that)
	MOV	R0, #YearCMOS+1
	BL	Read
	TEQ	R0, #19
	BNE	check20

; 20th century: year should be 95 to 99
	MOV	R0, #YearCMOS
	BL	Read
	CMP	r0,#95
	BLT	cmos_reset
	CMP	r0,#99
	BHI	cmos_reset
	B	checkboot

check20
	TEQ	R0, #20
	BNE	cmos_reset

; 21st century: year should <= 20
	MOV	R0, #YearCMOS
	BL	Read
	CMP	R0, #20
	BHI	cmos_reset

checkboot
; Bit 4 of DBTBCMOS should be 1 (Boot)
	MOV	R0, #DBTBCMOS
	BL	Read
	TEQ	R0, #(1:SHL:4)
	BNE	cmos_reset
 ]


; IF power-on bit set in IOC AND R/T/Del/Copy pressed THEN reset CMOS RAM
; note that memory cleared if POR, so key info has had plenty of time!
        MOV     R0, #IOC
        LDRB    R1, [R0, #IOCIRQSTAA]
        ANDS    R1, R1, #por_bit
        BEQ     no_cmos_reset

 [ CheckProtectionLink
        LDR     r0, =IOMD_MonitorType

; on Issue A's the protection bit is only weakly pulled up,
; so force it high, then read it back

        LDRB    r1, [r0]
        ORR     r1, r1, #IOMD_ProtectionLinkBit
        STRB    r1, [r0]

        LDRB    r1, [r0]
        TST     r1, #IOMD_ProtectionLinkBit
        BEQ     no_cmos_reset           ; if zero then CMOS is protected
 ]

 [ STB :LAND: IOMD_C_FrontPanelButton <> 0
  [ FrontPanelButtClearsCMOS
        MOV     r0, #IOMD_Base          ; if front panel button pressed then CMOS reset
        LDRB    r0, [r0, #IOMD_CLINES]
        TST     r0, #IOMD_C_FrontPanelButton
        BEQ     cmos_reset
  ]
 ]

        MOV     R0, #InitKbdWs
        LDR     R7, [R0, #R_Down_Flag]
        CMP     R7, #0
        BEQ     no_cmos_reset           ; power on bit checked again there

        [ :LNOT: STB
        ADD     sp, sp, #4              ; junk CannotReset flag from stack
        ]

; CMOS reset detectified.
; **************************************************************************
; Note: if this CMOS reset code ever needs changing again, it's probably
; better to rewrite it. The Compact apparently has a table of default
; CMOS values; R-p.o. just writes the table, DEL-p.o. zeroes all, then
; writes the table. With skipping of the time CMOS, and post-prodding of
; the sync, that would probably be a better algorithm.
; **************************************************************************

     SetBorder  R0, R1, 15, 0, 0        ; flash the border as warning!

        MOVS    R3, R7, LSR #16         ; full reset or just system?
        MOVNE   R3, #-1                 ; Del or Copy does all RAM
        MOVEQ   R3, #UserCMOS           ; R or T just system
    [ ChecksumCMOS
        BL      ValChecksum             ; unless the CMOS ram's corrupt ..
        MOVNE   R3, #-1                 ; .. then blast it anyway.
 [ STB
cmos_reset
        ADD     sp, sp, #4              ; junk CannotReset flag from stack
 ]
        MOVNE   R0, #0                  ; even the econet station number
        MOVEQ   R0, #1
    |
        MOV     R0, #1                  ; leave the econet station number
    ]
        MOV     R1, #0                  ; zero it first
cmrlp   BL      Write                   ; CMOS(R0) := R1
        ADD     R0, R0, #1
        CMP     R0, R3
        MOVEQ   R0, #&80                ; skip user cmos
        CMP     r0, #YearCMOS
        ADDEQ   r0, r0, #2
        CMP     r3, #UserCMOS           ; system only?
        BNE     skipskipforTC
        CMP     r0, #NewADFSCMOS
        CMPNE   r0, #CountryCMOS        ; skip these if so
        ADDEQ   r0, r0, #1
skipskipforTC
        CMP     R0, #CMOSLimit
        BNE     cmrlp
    [ ChecksumCMOS
        BL      MakeChecksum            ; create a valid checksum
    ]
; now put nice values in where necessary
; first full reset defaults
        CMP     r3, #-1
        BNE     not_full_reset

 [ STB :LAND: IOMD_C_PALNTSCType <> 0
        MOV     r4, #IOMD_Base          ; configure territory, country and timezone based on PAL/NTSC bit
        LDRB    r4, [r4, #IOMD_CLINES]
        MOV     r0, #TerritoryCMOS
        TST     r4, #IOMD_C_PALNTSCType
        MOVEQ   r1, #0                  ; PAL = territory UK
        MOVNE   r1, #49                 ; NTSC = territory USA
        BL      Write
        MOV     r0, #CountryCMOS
        TST     r4, #IOMD_C_PALNTSCType
        MOVEQ   r1, #1                  ; PAL = country UK
        MOVNE   r1, #48                 ; NTSC = country USA
        BL      Write
        MOV     r0, #TimeZoneCMOS
        TST     r4, #IOMD_C_PALNTSCType
        MOVEQ   r1, #0                  ; PAL = 0 from UTC (GMT)
        MOVNE   r1, #&E0                ; NTSC = -8 hours from UTC (USA Pacific)
        BL      Write
   [ STB :LAND: :DEF: ObsoleteNC1CMOS
        MOV     r0, #MiscellaneousNCCMOS
        TST     r4, #IOMD_C_PALNTSCType
        MOVEQ   r1, #0                  ; PAL = A4 paper size
        MOVNE   r1, #1                  ; NTSC = US letter paper size
        BL      Write
   ]
 |
        MOV     r0, #CountryCMOS
        MOV     r1, #1                  ; country UK
        BL      Write
 ]

 [ :LNOT: STB
        MOV     r0, #NewADFSCMOS
        MOV     r1, #&41                ; floppies=1, ST506=0, IDE=1 (changed 01-Sep-93)
        BL      Write
 ]

not_full_reset

; IF R or Delete pressed THEN set sync 0 ELSE set sync Auto
        MOV     R0, #InitKbdWs
        LDRB    R1, [R0, #R_Down_Flag]
        CMP     R1, #0
        LDREQB  R1, [R0, #Del_Down_Flag]
        CMPEQ   R1, #0
        MOV     R0, #VduCMOS
        MOVNE   R1, #MonitorTypeAuto :OR: Sync_Auto     ; if R or Del
        MOVEQ   R1, #MonitorTypeAuto :OR: Sync_Separate ; if T or Copy
        BL      Write

 [ MorrisSupport
        MOV     R8, #IOMD_Base
  [ {TRUE} ; cope with ARM7500FE
; Change test around so if not IOMD_Original, do the code,
; rather than checking for ARM7500 (which would exclude ARM7500FE)
	LDRB	r0, [r8, #IOMD_ID0]
	LDRB	r1, [r8, #IOMD_ID1]
	ORR	r0, r0, r1, LSL #8
	LDR	r1, =IOMD_Original
	TEQ	r0, r1
	BEQ	dont_program_mousetype
  |
        LDRB    R0, [R8, #IOMD_ID0]
        CMP     R0, #&E7
        LDRB    R0, [R8, #IOMD_ID1]
        CMPEQ   r0, #&5B
        BNE     dont_program_mousetype
  ]
;
; Morris based machines use PS2 mice/tracker balls
;
        MOV     R0, #MouseCMOS
        MOV     R1, #PointerDevice_PS2Mouse     ;type 3
        BL      Write

  [ Select16BitSound
; set print and sound CMOS (16bit sound)
        B       Config16BitSound
  ]
dont_program_mousetype
 ]

 [ Select16BitSound
        LDR     r0, =IOMD_MonitorType

; on Issue A's the protection bit is only weakly pulled up,
; so force it high, then read it back

        LDR     r1, [r0]
        ORR     r1, r1, #IOMD_SoundsystemLinkBit
        STR     r1, [r0]

        LDR     r1, [r0]
        TST     r1, #IOMD_SoundsystemLinkBit
        BEQ     Config16BitSound                ; if zero, must be Rimmer, so assume 16bit sound hardware present

; set print and sound CMOS (8bit sound)
        MOV     R0, #TutuCMOS
        MOV     R1, #2_0100  ; tbs chars valid, ctrlchars '|x'
        BL      Write
        B       ConfigSoundDone

Config16BitSound
; set print and sound CMOS (16bit sound)
        MOV     R0, #TutuCMOS
        MOV     R1, #2_10100100  ; tbs chars valid, ctrlchars '|x'
        BL      Write

ConfigSoundDone
 ]

        ADR     R8, DefaultCMOSTable
50
        LDRB    R0, [R8], #1
        CMP     R0, #&FF
        BEQ     hard_reset              ; power on bit musta bin set
        LDRB    R1, [R8], #1
        BL      Write
        B       %BT50

        LTORG

 [ ValidateCMOS :LAND: STB
DefaultCMOSTable ; list of non-zero options wanted :
; byte pairs of offset, value
; terminated by offset &FF
        =       KeyDelCMOS,             32
        =       KeyRepCMOS,             8
        =       MODETVCMOS,             &10                                     ; TV 0,1
        =       StartCMOS,	        (2:SHL:3)				; NOCAPS
        =       DBTBCMOS,	        (1:SHL:4)				; Boot
        =       YearCMOS,               97
        =       YearCMOS+1,             19
  [ IOMD_C_MonitorType = 0 :LAND: IOMD_C_PALNTSCType = 0
; TV if we don't have a MonitorType auto-detect bit
	=	VduCMOS,	        Sync_Separate :OR: MonitorType0
  |
; auto-detect if we have a MonitorType auto-detect bit
	=	VduCMOS,	        Sync_Auto :OR: MonitorTypeAuto
  ]
        =       MouseStepCMOS,          2
        =       SystemSpeedCMOS,	(1:SHL:2):OR:(1:SHL:4):OR:(0:SHL:5)
                                      ; Delete-etc reset
                                      ;              WimpMode auto
                                      ;                           Cache on
 | ;ValidateCMOS
DefaultCMOSTable ; list of non-zero options wanted :
; byte pairs of offset, value
; terminated by offset &FF
        =       KeyDelCMOS,     32
        =       FileLangCMOS,   8
        =       FontCMOS,       16      ; TMD 15-Dec-93: Changed to 64K from 32K - fixes MED-01774
        =       PigCMOS,        10
        =       KeyRepCMOS,     8
        =       RMASizeCMOS,    0
        =       SpriteSizeCMOS, 0
        =       MODETVCMOS,     &10     ; TV 0,1
        =       NetFSIDCMOS,    254
        =       NetPSIDCMOS,    235
        =       PSITCMOS,      (3:SHL:2) :OR: (1:SHL:5)
                              ; Baud 3
                              ;                print 1

        =       DBTBCMOS,      (1:SHL:4) :OR: (4:SHL:5)
                              ; Boot (changed from NoBoot 01-Sept-93)
                              ;                Data 4

        =       StartCMOS,     (4:SHL:0) :OR: (2:SHL:3) :OR: (1:SHL:6)
                              ; Drive 4 (changed from Drive 0 01-Sept-93)
                              ;                NOCAPS (changed from CAPS 02-May-91)
                              ;                               NODIR

     [ NewClockChip                     ; only on A1's!
        =       NewADFSCMOS+1,  &FF     ; step 3 for each drive
     ]

        =       NewADFSCMOS+2,  1       ; ADFSBuffers 1
        =       SoundCMOS,      &F0     ; speaker on, volume 7, channel 1

        =       LanguageCMOS,   ConfiguredLang
        =       YearCMOS,       97      ; changed from 95 to 97 on 02-Jan-97
        =       YearCMOS+1,     19
 [ :LNOT: Select16BitSound
        =       TutuCMOS,       2_0100  ; tbs chars valid, ctrlchars '|x'
 ]
        =       NetFilerCMOS,  (0:SHL:0) :OR: (1:SHL:1) :OR: (0:SHL:2)
                              ; FS list order by name
                              ;                Use $.Arthurlib
                              ;                               Large icons

     ;  =       Mode2CMOS,      WimpModeAutoBit :OR: CMOSResetBit ;AKA SystemSpeedCMOS - removed by RManby and SCormie 8/3/95
        =       DesktopCMOS,    2_01000000      ; verbose ON
        =       WimpFlagsCMOS,  2_01101111      ; instant effects, drags off screen
        =       ProtectionCMOS, 2_01110110      ; allow only peek and user RPC
        =       MouseStepCMOS,  2
        =       FileSwitchCMOS,(1:SHL:0) :OR: (1:SHL:1) :OR: (0:SHL:2) :OR: (0:SHL:3) :OR: (0:SHL:6)
                              ; truncate names
                              ;                Use DragASprite (changed 01-Sept-93)
                              ;                               Interactive file copying
                              ;                                              Wimp dither colours off
                              ;                                                             last shutdown ordinary

        =       DesktopFeaturesCMOS,(1:SHL:0) :OR: (8:SHL:1) :OR: (0:SHL:7)
                              ;      3D look
                              ;                     Homerton.Medium
                              ;                                    tiled window background

        [ STB
        =       SystemSpeedCMOS,(1:SHL:0):OR:(0:SHL:1):OR:(1:SHL:2):OR:(0:SHL:3):OR:(1:SHL:4):OR:(0:SHL:5):OR:(1:SHL:6):OR:(0:SHL:7)
                              ;  AUN ROMBoot Enabled
                              ;               AUN auto-station numbering off
                              ;                            Delete-etc reset
                              ;                                         power saving off
                              ;                                                      WimpMode auto
                              ;                                                                  Cache on
                              ;                                                                               Broadcast loader disabled
                              ;                                                                                            broadcast loader colours off
        |
        =       SystemSpeedCMOS,(0:SHL:0):OR:(0:SHL:1):OR:(1:SHL:2):OR:(0:SHL:3):OR:(1:SHL:4):OR:(0:SHL:5):OR:(1:SHL:6):OR:(0:SHL:7)
                              ;  AUN BootNet Disabled
                              ;               AUN auto-station numbering off
                              ;                            Delete-etc reset
                              ;                                         power saving off
                              ;                                                      WimpMode auto
                              ;                                                                  Cache on
                              ;                                                                               Broadcast loader disabled
                              ;                                                                                            broadcast loader colours off
        ]

        [ :LNOT: STB
        =       FontMaxCMOS,    64      ; 256k
        =       FontMax2CMOS,   &28     ; 36 point
        =       FontMax3CMOS,   &3C     ; 36 point
        |
        ;       yes, omitting FontMaxCMOS is deliberate!
        =       FontMax2CMOS,   &2C     ; 32 point
        =       FontMax3CMOS,   &38     ; 32 point
        ]
        =       AlarmAndTimeCMOS,2_00010000 ; !Alarm autosave on
        =       FSLockCMOS+5,   &EA     ; Checksum for no password
        =       CDROMFSCMOS,    &60     ; drives = 0, buffer size = 32K
  ]
        =       &FF
        ALIGN


no_cmos_reset                           ; R1 has por_bit set if power on
        Push    "r1"
        MOV     r0, #SystemSpeedCMOS
        BL      Read
        BIC     r1, r0, #CMOSResetBit   ; clear bit indicating CMOS reset
        MOV     r0, #SystemSpeedCMOS
        BL      Write
        Pull    "r1"

        Pull    r0                      ; always pull CannotReset flag
 [ SoftResets
        TST     r1, #por_bit
        BNE     hard_reset              ; it was a power-on, so it's a hard reset
        CMP     r0, #0
 [ DebugForcedReset
        MOVNE   r2, #Reset_CannotResetFlag
 ]
        BNE     hard_reset_forced

; IF control pressed OR memory implausible (Check SysHpd, CAM map sensible) THEN hard reset

        LDR     R0, =SysHeapStart
        LDR     R8, [R0, #:INDEX: hpdmagic]
        LDR     R2, =magic_heap_descriptor
        CMP     R8, R2                  ; check sysheap initialised
 [ DebugForcedReset
        MOVNE   r2, #Reset_SysHeapCorrupt
 ]
        BNE     hard_reset_forced

; also check CAM map sensible

 [ {TRUE}                               ; new code which allows for MEMC2's small pages
        MOV     R5, #0
        LDR     R4, [R5, #Page_Size]    ; R4 = page size
        ADRL    R3, PageShifts-1
        LDRB    R4, [R3, R4, LSR #12]   ; R4 = log2(pagesize)
        LDR     R3, [R5, #RAMLIMIT]     ; R3 = total RAM size
        MOV     R2, R3, LSR R4          ; number of pages=total memory / pagesize
        CMP     R2, #256                ; but if fewer than 128 (eg 64 on A305) (NB if <256 then <=128)
        MOVCC   R2, #128                ; then use 128 (all MEMC1's pages need initialising,
                                        ; even if half of them are not in use)
        SUB     R2, R2, #1
 [ MEMM_Type = "ARM600"
        LDR     R3, =CamEntriesForVicky
 |
        LDRLS   R3, =CamEntries         ; if <= 256 pages, then should be using
                                        ;  low-down cam soft copy
        LDRHI   R3, =CamEntriesForBigMachines ; else should be using hi up one
 ]

        LDR     R4, [R5, #MaxCamEntry]  ; get highest CAM entry
        LDR     R5, [R5, #CamEntriesPointer] ; and pointer to CAM soft copy
 |
        MOV     R5, #0
        LDR     R3, [R5, #RAMLIMIT]     ; read amount of RAM
        LDR     R4, [R5, #MaxCamEntry]  ; and highest CAM entry
        LDR     R5, [R5, #CamEntriesPointer] ; and pointer to CAM soft copy

        CMP     R3, #8*1024*1024        ; if >= 8MBytes then there are
        MOVCS   R2, R3, LSR #15         ; RAM/32k pages
        MOVCC   R2, #128                ; else there are 128 pages
        SUB     R2, R2, #1              ; what highest cam index should be

        LDRLS   R3, =CamEntries         ; if <= 8MBytes, should be using
                                        ; low-down cam soft copy
        LDRHI   R3, =CamEntriesForBigMachines ; else should be using hi up one
 ]

        CMP     R5, R3                  ; if not the same
 [ DebugForcedReset
        MOVNE   r2, #Reset_WrongCamMapAddress
        BNE     hard_reset_forced
 ]
        CMPEQ   R4, R2                  ; or number of pages not the same
 [ DebugForcedReset
        MOVNE   r2, #Reset_WrongNumberOfPages
 ]
        BNE     hard_reset_forced       ; then do a hard reset

; now check all cam contains sensible values

        MOV     R4, #0
        LDR     R4, [R4, #Page_Size]
        SUB     R4, R4, #1
 [ CPU_Type = "ARM600"
        ORR     R4, R4, #&F0000000      ; can have addresses above 64M
 |
        ORR     R4, R4, #&FC000000
 ]
CamCheck
        LDR     R3, [R5, R2, LSL #2]
        BIC     r3, r3, #&F0000000      ; remove PPL
        TST     R3, R4
 [ DebugForcedReset
        MOVNE   r2, #Reset_CamMapCorrupt
 ]
        BNE     hard_reset_forced       ; wally entry: not pagesize multiple, or >= 32M
        SUBS    R2, R2, #1
        BPL     CamCheck

; leave CTRL test till last, so the keyboard's had as much time to
; wiggle the wet string as we can give it
        MOV     R0, #InitKbdWs
        LDRB    R1, [R0, #CTRL_Down_Flag]
        CMP     R1, #0
        BNE     hard_reset

soft_reset
; clear out 4K of scratchspace, to use as a reverse CAM soft copy;
; set bytes to indicate page mapped to that address. Can then recalculate
; end of memory.

; This code doesn't currently work on ARM600 versions -
; 4K of workspace isn't enough to do this with a 4K page size
; We'd probably want to do it differently anyway, using the L2PT
; But since we're removing soft resets it's not worth the effort

        ASSERT  MEMM_Type <> "ARM600"

        MOV     R5, #ScratchSpace
        MOV     R1, #4*1024
        MOV     R2, #0
clrscratch
        SUBS    R1, R1, #4
        STRPL   R2, [R5, R1]
        BPL     clrscratch

        LDR     R2, [R2, #Page_Size]
        ADRL    R8, PageShifts-1
        LDRB    R8, [R8, R2, LSR #12]

        MOV     r7, #0
        LDR     r2, [r7, #RAMLIMIT]
        MOV     r2, r2, LSR r8          ; last valid page
        SUB     r2, r2, #1

        LDR     R7, [R7, #CamEntriesPointer]
        LDR     R12, =DuffEntry

restoreCAMloop
        LDR    R3, [R7, R2, LSL #2]
        MOV    r11, r3, LSR #28
        BIC    r3, r3, #&F0000000

        MOV    R0, R3, LSR R8              ; logram page number
        LDRB   R4, [R5, R0]
        CMP    r4, #0                      ; check for doubly mapped pages
        BEQ    rclon

        ORR    r3, r12, #&30000000         ; force to invalid place if so.
        STR    r3, [R7, R2, LSL #2]
        MOV    r3, r12
        MOV    r11, #3                     ; protected
        MOV    R0, R3, LSR R8
        LDRB   R4, [R5, R0]
rclon
        CMP    r3, #16*1024*1024           ; in application space?
        MOVLT  r11, #0                     ; noprot if so
        STRLT  r3, [R7, R2, LSL #2]
        ADD    R4, R4, #1
        STRB   R4, [R5, R0]                ; sema for interesting pages
        BL     BangCam
        SUBS   R2, R2, #1
        BPL    restoreCAMloop

; now do post-scan to see if we need to do more CAM bashing to get pages back.
; any entries that aren't validateable should be remapped.

        MOV     R7, #0
        MOV     R12, #ScratchSpace
        LDR     R2, [R7, #Page_Size]
findapplend
        LDRB    R3, [R12], #1
        CMP     R3, #0
        ADDNE   R7, R7, R2
        BNE     findapplend
        MOV     R1, #0
        STR     R7, [R1, #AplWorkSize]  ; verified value
        LDR     R3, [R1, #RAMLIMIT]     ; calc last valid page:
        MOV     R3, R3, LSR R8          ; RAMLIMIT >> R8
        MOV     R11, #0                 ; no PPL
        LDR     R4, [R11, #CamEntriesPointer]
testforremap
        SUBS    R3, R3, #1
        BMI     finishedremap
        LDR     R0, [R4, R3, LSL #2]
        BIC     r0, r0, #&F0000000      ; remove PPL
        ADD     R1, R0, R2
        SWI     XOS_ValidateAddress
        BCC     testforremap

        Push    "R2-R4"
        MOV     R0, R0, LSR R8          ; curr logram page number
        LDRB    R4, [R5, R0]
        SUB     R4, R4, #1
        STRB    R4, [R5, R0]            ; dec sema
        MOV     R2, R3                  ; entry no
        MOV     R3, R7                  ; addr to set to
        BL      BangCamUpdate
        Pull    "R2-R4"
holefilled
        ADD     R7, R7, R2
        LDRB    R0, [R12], #1           ; reinspect our reverse map
        CMP     R0, #0
        BNE     holefilled
        MOV     R0, #0
        STR     R7, [R0, #AplWorkSize]
        B       testforremap

finishedremap
        MOV     R12, #NVECTORS-1
        LDR     R11, =VecPtrTab
freenextvec
        LDR     R2, [R11, R12, LSL #2]
loseveclink
        LDR     R3, [R2, #TailPtr]
        BL      FreeSysHeapNode
        MOVVC   R2, R3
        BVC     loseveclink
        CMP     R3, #0                  ; were we at the end of the chain?
 [ DebugForcedReset
        MOVNE   r2, #Reset_VectorChainCorrupt
 ]
        BNE     hard_reset_forced
        SUBS    R12, R12, #1
        BPL     freenextvec
; so that's the code for restore default vectors, basically
        BL      InitVectors
        MOV     R0, #0
        LDR     R1, [R0, #AplWorkSize]
        STR     R1, [R0, #MemLimit]

        LDR     R3, =TickNodeChain
        LDR     R2, [R3]

loseticknodes
        CMP     R2, #0
        BEQ     ticknodesallgone

        LDR     R3, [R2]
        BL      FreeSysHeapNode
 [ DebugForcedReset
        MOVVS   r2, #Reset_TickNodesCorrupt
 ]
        BVS     hard_reset_forced
        MOV     R2, R3
        B       loseticknodes

ticknodesallgone
; and now it's time to free the IRQ structures

        MOV     R12, #(NoInterrupt-1)*12+8      ; last device link offset
        LDR     R11, =DefaultIRQ1V-DefaultIRQ1Vcode+Devices
freenextdev
        LDR     R2, [R11, R12]
losedevlink
        CMP     R2, #0
        BEQ     stepdevice

        LDR     R3, [R2, #8]
        BL      FreeSysHeapNode
 [ DebugForcedReset
        MOVVS   r2, #Reset_DeviceVectorCorrupt
 ]
        BVS     hard_reset_forced
        MOV     R2, R3
        B       losedevlink
stepdevice
        SUBS    R12, R12, #12
        BPL     freenextdev

; now the PIRQ structures and CallBack_Vector
        LDR     R11, =PIRQ_Chain
        MOV     r4, #PodDesp_Link
losepirqchain
        LDR     R2, [R11]
        CMP     r2, #0                  ; for CallBack_Vector
        BEQ     doobry
losepirqlink
        LDR     R3, [R2, r4]
        BL      FreeSysHeapNode
        MOVVC   R2, R3
        BVC     losepirqlink
        CMP     R3, #0                  ; were we at the end of the chain?
 [ DebugForcedReset
        MOVNE   r2, #Reset_PoduleOrCallBackCorrupt
 ]
        BNE     hard_reset_forced
        LDR     R2, =PIRQ_Chain
        CMP     R11, R2
        LDR     R2, =PFIQasIRQ_Chain
        MOVEQ   R11, R2
        CMPNE   r11, r2
        LDREQ   r11, =CallBack_Vector
    [ PodDesp_Link <> 0
        MOVEQ   r4, #0
    ]
        BEQ     losepirqchain


doobry  Pull    "R1"                    ; IOCControl restoration
        MOV     R0, #0
        STRB    R1, [R0, #IOCControlSoftCopy]
        MOV     R0, #IOC                ; and bash the hardware
        STRB    R1, [R0, #IOCControl]

        MOV     R0, #SoftReset
        B       ResetPart1Done

 |

; if soft resets are disabled, drop thru into hard reset code

 ] ; end of code to do with soft resets

hard_reset
 [ DebugForcedReset
        MOV     r2, #0                  ; indicate normal hard reset
 ]
hard_reset_forced
 [ DebugForcedReset
        STR     r2, [r0, -r0]           ; store to logical address zero
 ]

        Pull    "R2"                    ; discard old IOC state

; fill in relevant CamMap entries, so can soft start.

        MOV     R8, #0
        LDR     R8, [R8, #Page_Size]
        ADRL    R1, PageShifts-1
        LDRB    R1, [R1, R8, LSR #12]

 [ ExpandedCamMap
        MOV     r2, #0
  [ MEMM_Type = "ARM600"
        LDR     r0, [r2, #VideoSize]    ; offset from start of physical pages to static part
  |
        LDR     r0, =480*1024
  ]
        MOV     r0, r0, LSR r1          ; r0 := cam entry number
        MOV     r0, r0, LSL #3          ; r0 := offset into CAM map for start of static part

        MOV     R7, #32*1024*8
        MOV     R7, R7, LSR R1          ; r7 := cam entry offset for 32K

 |
        MOV     R0, #(512-32)*1024*4
        MOV     R0, R0, LSR R1          ; r0 := cam entry number * 4

        MOV     R7, #32*1024*4
        MOV     R7, R7, LSR R1          ; r7 := cam entry offset for 32K

        MOV     R2, #0
 ]
        LDR     R12, [R2, #RAMLIMIT]    ; R12 = total RAM size

                                        ; new code which allows for MEMC2's small pages
        MOV     R1, R12, LSR R1         ; R1 = number of pages
        CMP     R1, #256                ; but if fewer than 128 (eg 64 on A305) (NB if <256 then <=128)
        MOVCC   R1, #128                ; then use 128 (all MEMC1's pages need initialising,
                                        ; even if half of them are not in use)

        SUB     R3, R1, #1
        STR     R3, [R2, #MaxCamEntry]

 [ MEMM_Type = "ARM600"
        LDR     R1, =CamEntriesForVicky
 |
        LDRLS   R1, =CamEntries                 ; if <=256 pages (was if <=8MBytes) then small CAM copy
        LDRHI   R1, =CamEntriesForBigMachines   ; else use big CAM copy
 ]
        STR     R1, [R2, #CamEntriesPointer]


 [ MEMM_Type = "ARM600"

; On ARM600 we must zap all the soft CAM map before adding any entries,
; since the old contents are used in BangCamUpdate

        Push    "r0"
        ADD     r2, r1, r3, LSL #3      ; r2 -> last entry to do
        LDR     r0, =DuffEntry
        MOV     lr, #AP_Duff            ; PPL = no access
WallopDuffOnes
        STMDA   r2!, {r0, lr}           ; store address, PPL
        CMP     r2, r1
        BCS     WallopDuffOnes
        Pull    "r0"
 ]
        ADD     R0, R0, R1

        MOV     R2, #CursorChunkAddress
        LDR     r1, =AP_CursorChunk
        BL      AddCamEntries

        CMP     R12, #512*1024
        SUBEQ   R0, R0, R7              ; previous entries
        ADDNE   R0, R0, R7              ; next entries
        MOV     R2, #0
        MOV     r1, #AP_PageZero
        BL      AddCamEntries

        CMP     R12, #512*1024
        SUBEQ   R0, R0, R7              ; previous entries
        ADDNE   R0, R0, R7              ; next entries
        MOV     R2, #SysHeapChunkAddress
        MOV     r1, #AP_SysHeap :OR: PageFlags_Unavailable
        BL      AddCamEntries

 [ MEMM_Type = "ARM600"
        ADD     R0, R0, R7              ; next entries (ignore 512K machines)
        MOV     r7, #0
        LDR     r7, [r7, #L2PTSize]
        MOV     r7, r7, LSR #12-3       ; number of pages * 8
        LDR     R2, =L2PT
        LDR     r1, =AP_L2PT :OR: PageFlags_Unavailable
        BL      AddCamEntries

        ADD     r2, r0, #((L1PT-L2PT):SHR:(12-3)) + 4   ; point at PPL for 1st L1 page
        LDR     r1, =AP_L1PT
        STR     r1, [r2], #8            ; store 4 CAM entries for 4 x 4K = 16K of L1
        STR     r1, [r2], #8            ; mark them as unavailable for removal
        STR     r1, [r2], #8
        STR     r1, [r2], #8

        ADD     R0, R0, R7              ; add on enough pages for L2PT

        MOV     r7, #0
        LDR     r7, [r7, #SoftCamMapSize] ; number of bytes in soft cam map
        ADD     r7, r7, #UndStackSize
        MOV     r7, r7, LSR #12-3       ; number of bytes of cam map for this
        LDR     R2, =UndStackSoftCamChunk
        MOV     r1, #AP_UndStackSoftCam
        BL      AddCamEntries
 |
  [ MEMM_Type = "MEMC2"
; CCs from CMP still valid
        SUBEQ   R0, R0, R7              ; previous entries
        ADDNE   R0, R0, R7              ; next entries
        LDR     R2, =CursorChunkAddress+64*1024 ; 01F10000
        MOV     r1, #AP_CursorChunk
        BL      AddCamEntries
  ]
 ]

; let's boogie with the CMOS for a bit
; read info and move as much memory as we can

        BL      InitDynamicAreas

 [ NewStyle_Screen
        Push    "r0-r12"
        MOV     r0, #ScreenSizeCMOS
        BL      Read

        MOV     r5, #0
        LDR     r10, [r5, #Page_Size]   ; needed by MassageScreenSize
        MUL     r0, r10, r0             ; convert to bytes
        LDR     r5, [r5, #VideoSize]    ; maximum size
        BL      MassageScreenSize

        MOV     r1, #ChangeDyn_Screen   ; area number
        MOV     r2, r0                  ; initial size
        MOV     r3, #ScreenEndAdr       ; base address (start of 2nd copy)
        LDR     r4, =AP_Screen          ; area flags
        ADRL    r6, DynAreaHandler_Screen ; handler
        VDWS    r7                      ; workspace pointer
        MOV     r8, #0
        STR     r8, [r7, #CursorFlags]  ; put zero in CursorFlags as an indication that VDU not yet inited
        STR     r2, [r7, #TotalScreenSize]
        ADRL    r8, AreaName_Screen     ; area name
        BL      DynArea_Create
        Pull    "r0-r12"
 |
        MOV     R2, #0                  ; CAM entry number to use (irrelevant for new code)
        MOV     R0, #ScreenSizeCMOS     ; CMOS byte to read
        MOV     R3, #ScreenEndAdr       ; where to put it (screen fudged)
        LDR     R1, =VduDriverWorkSpace+TotalScreenSize
                                        ; variable to save size set
        MOV     r11, #AP_Screen         ; Unprotected
        BL      ReadCMOSAndConfigure    ; Screen must be done first to get video RAM pages
 ]

 [ NewStyle_RMA
        Push    "r0-r12"
        MOV     r1, #ChangeDyn_RMA      ; Area number
        MOV     r2, #4096               ; Initial size
        MOV     r3, #RMAAddress         ; Base address
        MOV     r4, #AP_RMA             ; Area flags
        MOV     r5, #RMAMaxSize         ; Maximum size
        ADRL    r6, DynAreaHandler_RMA  ; Pointer to handler
        MOV     r7, r3                  ; Workspace ptr points at area itself
        ADRL    r8, AreaName_RMA        ; 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"
 |
        MOV     R0, #1                  ; One page. Unprotected
        MOV     R3, #RMAAddress
        ADD     R1, R3, #:INDEX: hpdend ; see comment about SysHeap!
        MOV     r11, #AP_RMA
        BL      FudgeConfigureRMA
 ]

 [ NewStyle_SpriteArea
        Push    "r0-r12"
        MOV     r0, #0                  ; initialise SpriteSize to zero
        STR     r0, [r0, #SpriteSize]   ; (fixes bug MED-00811)

        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, #AP_Sprites         ; Area flags
        MOV     r5, #-1                 ; Maximum size
        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!
        Pull    "r0-r12"
 |
        MOV     R0, #SpriteSizeCMOS
        MOV     R3, #SpriteSpaceAddress
        LDR     R1, =SpriteSize         ; for Richard. Unprotected
        MOV     r11, #AP_Sprites
        BL      ReadCMOSAndConfigure
 ]

 [ NewStyle_RAMDisc
        Push    "r0-r12"
        MOV     r0, #RAMDiscCMOS        ; find out how much RAM disc configured
        BL      GetConfiguredSize       ; in: r0 = CMOS address, out: r2 = size

        MOV     r1, #ChangeDyn_RamFS    ; Area number
        MOV     r3, #-1                 ; Base address dynamic
        MOV     r4, #AP_RAMDisc         ; Area flags
        MOV     r5, #16*1024*1024       ; Limit maximum size to 16MB until JSR fixes FileCore
        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!
        Pull    "r0-r12"
 |
        MOV     R0, #RAMDiscCMOS
        MOV     R3, #RAMDiscAddress
        LDR     R1, =RAMDiscSize        ; for RAMFS
        MOV     r11, #AP_RAMDisc        ; protected
        BL      ReadCMOSAndConfigure
 ]

 [ NewStyle_FontArea
        Push    "r0-r12"
        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, #AP_FontArea        ; Area flags
        MOV     r5, #-1                 ; Maximum size
        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"
 |
        MOV     R0, #FontCMOS
        MOV     R3, #FontCacheAddress
        LDR     R1, =FontCacheSize        ; for Fontmanager
        MOV     r11, #AP_FontArea
        BL      ReadCMOSAndConfigure      ; still protected
 ]

; get here with R2 = highest CAM entry used (in old model)

 [ MEMM_Type = "ARM600"
        LDR     R0, =(AplWorkMaxSize-32*1024):SHR:12    ; maximum number of pages in aplspace
        MOV     R3, #32*1024            ; aplwork start
        LDR     R1, =AplWorkSize        ; aplwork size
        MOV     r11, #AP_AppSpace
        BL      FudgeConfigureRMA       ; put as much as possible in aplspace

  [ :LNOT: GetPagesFromFreePool         ; memory is already in free pool, so don't mess around
        MOV     r0, #0
        LDR     r0, [r0, #RAMLIMIT]
        MOV     r0, r0, LSR #12         ; only limit on free pool is ram size
        MOV     R3, #FreePoolAddress
        LDR     R1, =FreePoolDANode + DANode_Size
        MOV     r11, #AP_FreePool
        BL      FudgeConfigureRMA       ; and the rest in FreePool
  ]
 |
        MOV     R0, #4096               ; try and get maximum amount possible
        MOV     R3, #32*1024            ; aplwork start
        LDR     R1, =AplWorkSize        ; aplwork size
        MOV     r11, #AP_AppSpace
        BL      FudgeConfigureRMA       ; stuff rest on AplWork
 ]

        MOV     R0, #0
        LDR     R1, [R0, #AplWorkSize]
        ADD     R1, R1, #32*1024
        STR     R1, [R0, #AplWorkSize]
        STR     R1, [R0, #MemLimit]

 [ MEMM_Type <> "ARM600"

; Now add all the NaffEntries to CamEntries
; R2 highest CAM entry used
; NB This is not done on ARM600, as the whole soft CAM map has to be zapped before
; we start, because BangCamUpdate uses the old contents

        LDR     R3, [R0, #MaxCamEntry]  ; (R0 still zero from above)
        CMP     R2, R3
        BGT     CAMallset
        LDR     R0, [R0, #CamEntriesPointer] ; (R0 still zero from above)
 [ ExpandedCamMap
        ADD     r3, r0, r3, LSL #3      ; r3 -> last pair of words to do
        ADD     r0, r0, r2, LSL #3      ; r0 -> first pair of words to do
        LDR     r1, =DuffEntry          ; address
        MOV     r2, #AP_Duff            ; PPL
WallopDuffOnes
        STMIA   r0!, {r1, r2}           ; store address, PPL
        CMP     r0, r3
        BLS     WallopDuffOnes
 |
        LDR     R1, =DuffEntry :OR: (AP_Duff :SHL: 28)
WallopDuffOnes
        STR     R1, [R0, R2, LSL #2]
        ADD     R2, R2, #1
        CMP     R2, R3
        BLE     WallopDuffOnes
 ]
CAMallset
 ]

 [ :LNOT: NewCDA
; System heap only initialised here on older software models
; On new ones it's already been initialised by hand in InitDynamicAreas,
; so that we can create dynamic areas above here.

        MOV     R0, #HeapReason_Init    ; initialise system heap
        LDR     R1, =SysHeapStart
        MOV     R3, #32*1024 - (SysHeapStart-SysHeapChunkAddress)
        STR     R3, [R1, #:INDEX: hpdend] ; keep ValidateAddress happy
        SWI     XOS_Heap                  ; tuff if it fails
 ]
        BL      InitVectors               ; ready for OsByte to read mode

 [ :LNOT: GetPagesFromFreePool
        MOV     R0, #0                  ; initialise module list to empty
        STR     R0, [R0, #Module_List]
 ]
        LDR     R1, =ModuleSWI_HashTab
        MOV     R2, #ModuleSHT_Entries-1
clearmswis
        STR     R0, [R1, R2, LSL #2]
        SUBS    R2, R2, #1
        BGE     clearmswis

     [  International
        MOV     R1, #-1                                 ; We don't have a message file yet !
        STRB    R1, [R0, #ErrorSemaphore]               ; Don't translate errors.
        STR     R0, [R0, #KernelMessagesBlock]          ; No message file open.
     ]

        MOV     R0, #IOC
        LDRB    R1, [R0, #IOCIRQSTAA]
        ANDS    R1, R1, #por_bit
        STRNEB  R1, [R0, #IOCIRQCLRA]   ; clear POR if set

        ; from STB, but generally useful
	MOV	R0, #0
	MOV	R1, R1, LSR #4
	STRB	R1, [R0, #ResetType]			; Save POR state in workspace (bit 0 of ResetType)


        ; Make the choice between PowerOn and Hard reset based purely on
        ; the state of the POR bit and NOT on whether memory was cleared.
 [ {FALSE}
        LDREQ   R0, =OsbyteVars + :INDEX: LastBREAK
        LDREQB  R0, [R0]
        TSTEQ   R0, #&80                ; tbs set if memory cleared
 ]
        MOVNE   R0, #PowerOnReset
        MOVEQ   R0, #ControlReset


ResetPart1Done                          ; R0 is reset type
        TEQP    PC, #SVC_mode + I_bit   ; interrupts off since kbd bash done
        LDR     R1, =OsbyteVars + :INDEX: LastBREAK
        STRB    R0, [R1]

        MOV     R1, #InitKbdWs
        LDRB    R0, [R1, #KB_There_Flag]
        LDRB    R1, [R1, #SHIFT_Down_Flag]
        Push    "R0, R1"                ; save until after MOSInit

; copy IRQ handler: not done with rest of copying
; because soft break needs the info to free any claimed blocks.

        LDR     R0, =DefaultIRQ1V
        ADRL    R1, DefaultIRQ1Vcode
        ADRL    R2, DefaultIRQ1Vcode_end
CopyDefaultIRQ1V
        LDR     R3, [R1], #4
        STR     R3, [R0], #4
        CMP     R1, R2
        BNE     CopyDefaultIRQ1V

        LDR     R0, =PIRQ_Chain
        ADRL    R1, Default_PIRQHandler_Node
        STR     R1, [R0]
        STR     R1, [R0, #PFIQasIRQ_Chain-PIRQ_Chain]
 ASSERT Default_PFIQasIRQHandler_Node = Default_PIRQHandler_Node

        MOV     R0, #0                  ; put in IRQ handler, word at 0
        STRB    r0, [r0, #FIQclaim_interlock]
        STRB    r0, [r0, #CallBack_Flag]
        STR     r0, [r0, #CallBack_Vector]

 [ CPU_Type = "ARM600"

; we're poking locations 0 and &18 here, so we'd best go back to SVC32

        mrs     AL, r2, CPSR_all
        BIC     r3, r2, #&1F
        ORR     r3, r3, #SVC32_mode
        msr     AL, CPSR_all, r3
 ]

 [ DebugForcedReset
        LDR     R1, [R0]
        TEQ     R1, #0                                  ; if normal hard reset
        LDREQ   R1, BranchThroughZeroInstruction2       ; then get branchthruzero code
 |
        LDR     R1, BranchThroughZeroInstruction2
 ]
        STR     R1, [R0]                                ; put branch through 0 code at 0

        LDR     R1, RealIRQHandler
        STR     R1, [R0, #&18]

 [ StrongARM
   ;for StrongARM, we need to do an IMB type thing for modifying code in vector area, and
   ;for copying irq handler code
        ARM_read_ID r1
        AND     r1,r1,#&F000
        CMP     r1,#&A000
        BNE     furtherpoke_notSA
   ;first, we clean one cache entry, 0..1F, = vector area
        MOV     r1,#0
        ARMA_clean_DCentry r1
   ;next, we clean DefaultIRQ1V code area
        LDR     r0,=DefaultIRQ1V
        ADD     r1,r0,#(DefaultIRQ1Vcode_end - DefaultIRQ1Vcode)
        ARMA_clean_DCrange r0,r1
   ;and then we synch IC
        ARMA_drain_WB
        ARMA_flush_IC
        MOV     r0,#0    ;restore r0 as zero base
furtherpoke_notSA
    [ ARM810support
        ARM8_branchpredict_flush  ;IMB on ARM810, and harmless on other ARMs
    ]
 ]

 [ CPU_Type = "ARM600"

; now back to SVC26

        msr     AL, CPSR_all, r2
 ]
        MOV     R1, #&100
        STR     R1, [R0, #RCLimit]
        STR     R0, [R0, #ReturnCode]
        STR     R0, [R0, #TickNodeChain]

;now put in error handler and escape handler
        BL      DEFHAN
        BL      DEFHN2
        MOV     R0, #ExceptionDumpArea
        LDR     R1, =DUMPER
        SWI     XOS_ChangeEnvironment

        VDWS    WsPtr                   ; main MOS initialisation
        BL      VduInit
        BL      ExecuteInit
        BL      KeyInit
        BL      MouseInit
        BL      OscliInit               ; before initialising modules

        TEQP    pc, #SVC_mode           ; enable IRQs
        NOP

        [ DoInitialiseMode :LOR: :LNOT: STB
        BL      InitialiseMode          ; select correct screen mode, in case any
                                        ; module prints anything in initialisation
        ]

        MOV     R0, #&FD                ; read last reset type
        MOV     R1, #0
        MOV     R2, #&FF
        SWI     XOS_Byte
        CMP     R1, #SoftReset          ; soft reset?
        BEQ     SkipHardResetPart2

; HardResetPart2
        BL      L1L2PTenhancements      ; little tricks on cacheability etc for performance
        BL      InitVariables
        BL      AMBControl_Init         ; initialise AMBControl section
        BL      ModuleInit              ; initialise modules
                                        ; scan podules, copy modules.

        MOV     R0, #0                  ; shrink sysheap as far as will go.
        SUB     R1, R0, #4*1024*1024
        SWI     XOS_ChangeDynamicArea
        MOV     R0, #ReadCMOS
        MOV     R1, #SysHeapCMOS
        SWI     XOS_Byte
        AND     R2, R2, #2_111111       ; mask to same size as status
        MOV     R0, #0
        LDR     R0, [R0, #Page_Size]
        MULTIPLY R3, R0, R2             ; size spare wanted
        BL      ClaimSysHeapNode
        MOV     R0, #HeapReason_Free
        SWI     XOS_Heap

        MOV     R0, #ReadCMOS
        MOV     R1, #FileLangCMOS
        SWI     XOS_Byte
        MOV     R1, R2
        MOV     R0, #FSControl_SelectFS ; set configured filing system
        SWI     XOS_FSControl

  [ DebugROMInit
        SWI     XOS_WriteS
        =       "Service_PostInit",0
        SWI     XOS_NewLine
  ]
        MOV     r1, #Service_PostInit   ; issue post-initialisation service
        BL      Issue_Service

; New code added here by TMD 01-Apr-92
; Go into user mode, issue a dummy SWI, then go back into SVC mode

  [ DebugROMInit
        SWI     XOS_WriteS
        =       "callbacks",0
        SWI     XOS_NewLine
  ]
        TEQP    PC, #0                  ; enter USR mode (IRQs, FIQs enabled)
        NOP                             ; wait for it to take effect
        SWI     XOS_WriteI+0            ; I hope it doesn't generate an error
                                        ; otherwise the callback will get deferred!
  [ DebugROMInit
        SWI     XOS_WriteS
        =       "EnterOS!",0
        SWI     XOS_NewLine
  ]
        SWI     XOS_EnterOS             ; switch back to SVC mode (IRQs, FIQs enabled)
  [ RO371Timings
        BL      finalmemoryspeed
  ]
; end of added code

      [ International                   ; Open the kernel messages file.
        ADR     r0, KernelMessagesBlock+4
        ADR     r1, MessageFileName
        MOV     r2, #0                  ; Use file directly.
        SWI     XMessageTrans_OpenFile
        MOVVC   r0, #-1
        STRVC   r0, [r0, #KernelMessagesBlock+1]  ; Message file is now open.
      ]

SkipHardResetPart2                      ; code executed on all types of reset
      [ International
        MOV     r0, #0
        LDR     r1, [r0, #KernelMessagesBlock] ; if we've managed to open message file
        TEQ     r1, #0
        STRNEB  r0, [r0, #ErrorSemaphore] ; then allow errors to be translated
      ]

    [ DoInitialiseMode :LOR: :LNOT: STB
        BL      InitialiseMode
     [ :LNOT: STB                      ; don't print stuff on STB type products
        SWI     XOS_WriteS
        =       10, "$SystemName ", 0   ; now RISC OS (no +) again
        ALIGN

        MOV     R0, #0
        LDR     R0, [R0, #RAMLIMIT]
      [ {TRUE}                          ; Give startup message in megabytes
        MOV     R0, R0, LSR #20         ; /(1024*1024)
        LDR     R1, =GeneralMOSBuffer
        MOV     R2, #?GeneralMOSBuffer
        SWI     XOS_ConvertInteger4
        SWI     XOS_Write0
        SWI     XOS_WriteS
        =       "MB", 10,13, 10, 0      ; title complete
        ALIGN
      |
        MOV     R0, R0, LSR #10         ; /1024
        LDR     R1, =GeneralMOSBuffer
        MOV     R2, #?GeneralMOSBuffer
        SWI     XOS_ConvertInteger4
        SWI     XOS_Write0
        SWI     XOS_WriteS
        =       "K", 10,13, 10, 0       ; title complete
        ALIGN
      ]
     ]
    ]

 [ StrongARM
  [ STB
   ! 0,"Printing of processor type disabled"
  |
        ;Print the processor type
        MOV     r0, #0
        LDRB    r0, [r0, #ProcessorType]
        TEQ     r0, #255
        BEQ     %FT01
        ADR     r1, processor_names
      [ International
        ADD     r0, r1, r0, LSL #3
	BL	Write0_Translated
	SWI	XOS_NewLine
	SWI	XOS_NewLine
      |
        ADD     r0, r1, r0, LSL #5
        SWI     XOS_Write0
      ]
   ]
 ]

01      MOV     r0, #0                  ; Set DomainId to 0 every reset
        STR     r0, [r0, #DomainId]     ; before calling anyone

; issue reset service call

        MOV     R1, #Service_Reset
        SWI     XOS_ServiceCall

; now set up the default FIQ owner

        MOV     R1, #Service_ClaimFIQ
        SWI     XOS_ServiceCall

        MOV     R1, #Service_ReleaseFIQ
        SWI     XOS_ServiceCall

 [ SoftResets :LAND: STB
        ! 0, "!!!! SoftReset is true => resets close all open files !!!!"
        MOV     R0, #FSControl_Shut     ; Open files get closed at reset
        SWI     XOS_FSControl
 ]

        BL      PostInit

        MOV     r0, #&FD                ; read last reset type (again!)
        MOV     r1, #0
        MOV     r2, #&FF
        SWI     XOS_Byte
        CMP     r1, #SoftReset          ; a softie?
        SWINE   XOS_WriteI+7            ; go beep! Yaay!

        CMP     r1, #PowerOnReset
        BNE     %FT75

 [ CheckProtectionLink
        LDR     r1, =IOMD_MonitorType   ; check link bit again
        LDRB    r1, [r1]                ; no need to preload bus, since should
        TST     r1, #IOMD_ProtectionLinkBit ; be still there from earlier
        BEQ     %FT75                   ; zero => protected
 ]


; if any monitor key pressed, reconfigure, otherwise hang around for a bit
; till keys get a chance to come in again after being reset for the umpteenth
; time by yet another keyboard handler! SKS 07-Jun-88

        MOV     r3, #0
        LDR     r3, [r3, #MetroGnome]
 [ RunningOnEmul :LAND: :LNOT: STB
        ADD     r3,r3,#1
 |
        ADD     r3, r3, #10             ; Hang about for a little while
 ]

KeypadStar_key  * -92

HorologicalDelayLoop1
        MOV     r0, #&79                        ; scan keyboard
        MOV     r1, #&FF                        ; starting at (&FF + 1) AND &FF
60
        ADD     r1, r1, #1
        AND     r1, r1, #&FF
        SWI     XOS_Byte
        TEQ     r1, #&FF                        ; if no key down
        BEQ     %FT70                           ; then check if we've run out of time

        ADR     r2, MonitorKeypadTable
62
        LDRB    r14, [r2], #2                   ; search for key in table
        TEQ     r14, #&FF
        BEQ     %FT70
        TEQ     r1, r14
        BNE     %BT62
        LDRB    r3, [r2, #-1]                   ; get corresponding CMOS bits
        MOV     r0, #ReadCMOS
        MOV     r1, #VduCMOS
        SWI     XOS_Byte
        BIC     r2, r2, #MonitorTypeBits
        ORR     r2, r2, r3
        MOV     r0, #WriteCMOS
        SWI     XOS_Byte

        TEQ     r3, #MonitorTypeAuto            ; if we're setting monitortype auto
        BNE     %FT64
        ADRL    r0, ModeCMOSTable +8            ; then configure mode auto
        LDR     r2, [r0, #-8]                   ; (load auto value)
        BL      WriteMultiField
        ADRL    r0, SyncCMOSTable +8            ; and configure sync auto
        LDR     r2, [r0, #-8]                   ; (load auto value)
        BL      WriteMultiField

64
    [ DoInitialiseMode :LOR: :LNOT: STB
        BL      InitialiseMode

      [ International
        SWI     XOS_WriteI+10
        BLVC    WriteS_Translated
        =       "MonType:Monitor type reconfigured.",10,13,10,0
        ALIGN
      |
        SWI     XOS_WriteS
        =       10,"Monitor type reconfigured.",10,13,10,0
        ALIGN
      ]
    ]
        B       %FT75

BranchThroughZeroInstruction2
 [ ProcessorVectors
        LDR     PC, .+ProcVec_Branch0
 |
        B       .+(RESET1-0)
 ]

MonitorKeypadTable      ; internal key number, CMOS bits
        =       106, MonitorType0
        =       107, MonitorType1
        =       124, MonitorType2
        =       108, MonitorType3
        =       122, MonitorType4
        =       123, MonitorType5
        =       26,  MonitorType6
        =       27,  MonitorType7
        =       42,  MonitorType8
        =       43,  MonitorType9
        =       76,  MonitorTypeAuto    ; keypad dot
        =       &FF
        ALIGN 32

 [ StrongARM
  [ STB
   ! 0,"Disabling Processor ID display"
  |
processor_names
   [ International
        =	"600", 0
	ALIGN	8
	=	"610", 0
	ALIGN	8
	=	"700", 0
	ALIGN	8
	=	"710", 0
	ALIGN	8
	=	"810", 0
	ALIGN	8
	=	"SA110", 0
	ALIGN	8
	=	"7500", 0
	ALIGN	8
	=	"7500FE", 0
	ALIGN	8
   |
        DCB     "ARM 600 Processor",10,13,10,0
        ALIGN 32
        DCB     "ARM 610 Processor",10,13,10,0
        ALIGN 32
        DCB     "ARM 700 Processor",10,13,10,0
        ALIGN 32
        DCB     "ARM 710 Processor",10,13,10,0
        ALIGN 32
        DCB     "ARM 810 Processor",10,13,10,0
        ALIGN 32
        DCB     "StrongARM Processor",10,13,10,0
        ALIGN 32
        DCB     "ARM 7500 Processor",10,13,10,0
        ALIGN 32
        DCB     "ARM 7500FE Processor",10,13,10,0
        ALIGN 32
   ]
        ; type, internal type, features
  ]
cputable
        DCD &6000,0,0
        DCD &6100,1,0
        DCD &7000,2,0
        DCD &7100,3,0
        DCD &8100,4,2_11101
 [ {TRUE}
   ;corrected for 3.71 (SA does not abort for vector reads in 26-bit mode)
        DCD &a100,5,2_11011
 |
   ;value for 3.70
        DCD &a100,5,2_11111
 ]
        DCD &7500,6,0
        DCD &7501,7,0
        DCD -1
 ]

      [ International
MessageFileName DCB     "Resources:$.Resources.Kernel.Messages",0
        ALIGN
      ]

 [ StrongARM
Processor_Type
        MOV     r0,#IOMD_Base
        LDRB    r1,[r0,#IOMD_ID0]
        CMP     r1,#&E7
        LDRB    r1,[r0,#IOMD_ID1]
        CMPEQ   r1,#&D4
        BEQ     PT_RiscPC               ; E7,D4 means Risc PC
        CMP     r1,#&5B
        MOVEQ   r0,#&7500               ; 5B means 7500
        BEQ     PT_lookup
        CMP     r1,#&AA
        MOVEQ   r0,#&7500
        ORREQ   r0,r0,#&0001            ; AA means 7500FE - mark as 7501
        BEQ     PT_lookup
PT_RiscPC
        ReadCop  R0,CR_ID               ; see data sheets for values
        ; ARM 600 funny
        TST     R0,#&f000
        MOVEQ   R0,R0, LSL #4
        AND     R0,R0,#&ff00
PT_lookup
        ADR     R1,cputable
66
        LDR     R2,[R1],#4
        TEQ     R2,#0
        MOVMI   R0,#0
        STRMIB  R2,[R0,#ProcessorType]
        MOVMI   PC,LR
        TEQ     R2,R0
        ADDNE   R1,R1,#8
        BNE     %BT66
        LDMIA   R1,{R0,R2}
        MOV     R1,#0
        STRB    R0,[R1,#ProcessorType]
        STRB    R2,[R1,#ProcessorFlags]
        MOV     PC,LR
 ]

70
        MOV     r14, #0
        LDR     r14, [r14, #MetroGnome]
        CMP     r14, r3
        BLO     HorologicalDelayLoop1
75


; Deal with SHIFT pressed/SHIFT-BREAK configured:
; do appropriate FSControl if wanted

        Pull    "R0"                    ; first check kbd there

        CMP     R0, #0
        BEQ     AutoBootCosNoKbd

        MOV     R0, #&FF
        MOV     R1, #0
        MOV     R2, #&FF                ; read shifty state
        SWI     XOS_Byte
        AND     R0, R1, #8              ; picka da bit
        EOR     R0, R0, #8              ; invert sense
        Pull    "R1"
        CMP     R1, #0
        MOVNE   R1, #8
        EORS    R1, R1, R0
        BEQ     %FT80

Hortoculture_Kicking
        MOV     R0, #FSControl_BootupFS
        SWI     XOS_FSControl
        BVC     %FT80

        Push    "r3,r4"
        ADD     r1, r0, #4              ; Set Boot$Error if it failed (Desktop will report it).
        ADR     r0, str_booterror
        MOV     r2, #1024               ; Big enough that terminator will be reached.
        MOV     r3, #0
        MOV     r4, #VarType_String
        SWI     XOS_SetVarVal
        SUBVS   r0, r1, #4              ; If setting Boot$Error failed then report original error as before.
        BLVS    PrintError
        SWIVS   XOS_NewLine
        Pull    "r3,r4"
80
; if either * pressed, drop into * prompt, otherwise hang around for a bit
; till keys get a chance to come in again after being reset for the umpteenth
; time by yet another keyboard handler! SKS 01-Jun-88

        MOV     r3, #0
        LDR     r3, [r3, #MetroGnome]
 [ RunningOnEmul :LAND: :LNOT: STB
        ADD     r3,r3,#1
        |
        ADD     r3, r3, #10             ; Hang about for a little while
 ]

HorologicalDelayLoop2
        MOV     r1, #KeypadStar_key :AND: &FF
        BL      IsKeyPressedAtReset
        BEQ     DoStartSuper            ; EQ -> start up supervisor

        MOV     r0, #0
        LDR     r0, [r0, #MetroGnome]
        CMP     r0, r3
        BLO     HorologicalDelayLoop2


; Start configured language module if keypad-* wasn't pressed

        MOV     R0, #ReadCMOS
        MOV     R1, #LanguageCMOS
        SWI     XOS_Byte

        MOV     R0, #ModHandReason_GetNames
        SUB     R1, R2, #1
        MOV     R2, #0                  ; preferred incarnation
        SWI     XOS_Module
        ADRVSL  R3, UtilityMod
        LDR     R2, [R3, #Module_Title]
        CMP     R2, #0
        ADDNE   R1, R3, R2
DoStartSuper
        ADREQL  R1, UtilModTitle        ; ALWAYS enter via SWI: sets CAO etc.
        MOV     R0, #ModHandReason_Enter
        ADRL    R2, crstring            ; no environment
        SWI     XOS_Module
        CMP     r0, r0                  ; set EQ if failed to enter config.lang
        B       DoStartSuper            ; -> force Super entry


str_booterror   DCB     "Boot$Error",0
                ALIGN


AutoBootCosNoKbd
    [ :LNOT: STB
      [ International
        SWI     XOS_WriteI+7
        BLVC    WriteS_Translated
        =       "NoKbd:No keyboard present - autobooting", 10,13,0
        ALIGN
      |
        SWI     XOS_WriteS
        =       7, "No keyboard present - autobooting", 10,13,0
        ALIGN
      ]
    ]
        B       Hortoculture_Kicking


RealIRQHandler
 [ ProcessorVectors
        LDR     PC, .-&18+ProcVec_IRQ
 |
        B     Initial_IRQ_Code+.-&18
 ]

; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; In    r1 = INKEY -ve key code to look for

; Out   EQ: key pressed
;       NE: key not pressed

IsKeyPressedAtReset ENTRY "r0-r2"

        MOV     r0, #129
        MOV     r2, #&FF
        SWI     XOS_Byte
        TEQ     r1, #&FF
        TEQEQ   r2, #&FF
        EXIT

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

    [ AddTubeBashers
TubeDumpR0     ROUT
        Push  "R1, R2, lr"
        ADR   lr, HexTable
        TubeChar r0, r1, "MOV r1, #"" """
        MOV    R1, #7
01      MOV    R0, R0, ROR #28
        AND    R2, R0, #&F
        TubeChar R0, R1, "LDRB R1, [lr, R2]"
        SUBS   R1, R1, #1
        BPL    %BT01
        TubeChar r0, r1, "MOV r1, #"" """
        Pull  "R1, R2, PC", ,^

TubeNewl
        TubeChar R0, R1, "MOV R1, #10"
        TubeChar R0, R1, "MOV R1, #13"
        MOVS   pc, lr

HexTable = "0123456789ABCDEF"

    ]

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

        END