root/sys/arch/amiga/amiga/locore.s
/*      $NetBSD: locore.s,v 1.190 2026/04/28 03:29:09 thorpej Exp $     */

/*
 * Copyright (c) 1988 University of Utah.
 * Copyright (c) 1980, 1990 The Regents of the University of California.
 * All rights reserved.
 *
 * This code is derived from software contributed to Berkeley by
 * the Systems Programming Group of the University of Utah Computer
 * Science Department.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * from: Utah $Hdr: locore.s 1.58 91/04/22$
 *
 *      @(#)locore.s    7.11 (Berkeley) 5/9/91
 *
 * Original (hp300) Author: unknown, maybe Mike Hibler?
 * Amiga author: Markus Wild
 * Other contributors: Bryan Ford (kernel reload stuff)
 */

#include "opt_fpu_emulate.h"
#include "opt_bb060stupidrom.h"
#include "opt_p5ppc68kboard.h"
#include "opt_compat_netbsd.h"
#include "opt_compat_sunos.h"
#include "opt_fpsp.h"
#include "opt_kgdb.h"
#include "opt_lockdebug.h"
#include "opt_lev6_defer.h"
#include "opt_m68k_arch.h"

#include "assym.h"
#include <machine/asm.h>
#include <machine/trap.h>

        .text
GLOBAL(kernel_text)
L_base:
        .long   0x4ef80400+PAGE_SIZE    /* jmp jmp0.w */
        .fill   PAGE_SIZE/4-1,4,0/*xdeadbeef*/

#include <amiga/amiga/vectors.s>
#include <amiga/amiga/custom.h>

#ifdef DRACO
#include <amiga/amiga/drcustom.h>
#endif

#define CIAAADDR(ar)    movl    _C_LABEL(CIAAbase),ar
#define CIABADDR(ar)    movl    _C_LABEL(CIABbase),ar
#define CUSTOMADDR(ar)  movl    _C_LABEL(CUSTOMbase),ar
#define INTREQRADDR(ar) movl    _C_LABEL(INTREQRaddr),ar
#define INTREQWADDR(ar) movl    _C_LABEL(INTREQWaddr),ar
#define INTENAWADDR(ar) movl    _C_LABEL(amiga_intena_write),ar
#define INTENARADDR(ar) movl    _C_LABEL(amiga_intena_read),ar

        .text
/*
 * This is where we wind up if the kernel jumps to location 0.
 * (i.e. a bogus PC)  This is known to immediately follow the vector
 * table and is hence at 0x400 (see reset vector in vectors.s).
 */
        pea     Ljmp0panic
        jbsr    _C_LABEL(panic)
        /* NOTREACHED */
Ljmp0panic:
        .asciz  "kernel jump to zero"
        .even

/*
 * Other exceptions only cause four and six word stack frame and require
 * no post-trap stack adjustment.
 */

/*
 * Interrupt handlers.
 *
 *      Level 0:        Spurious: ignored.
 *      Level 1:        builtin-RS232 TBE, softint (not used yet)
 *      Level 2:        keyboard (CIA-A) + DMA + SCSI
 *      Level 3:        VBL
 *      Level 4:        not used
 *      Level 5:        builtin-RS232 RBF
 *      Level 6:        Clock (CIA-B-Timers), Floppy index pulse
 *      Level 7:        Non-maskable: shouldn't be possible. ignore.
 */

/* Provide a generic interrupt dispatcher, only handle hardclock (int6)
 * and serial RBF (int5) specially, to improve performance
 */
ENTRY_NOPROFILE(spurintr)
        addql   #1,_C_LABEL(intr_depth)
        addql   #1,_C_LABEL(intrcnt)+0
        INTERRUPT_SAVEREG
        CPUINFO_INCREMENT(CI_NINTR)
        INTERRUPT_RESTOREREG
        subql   #1,_C_LABEL(intr_depth)
        jra     _ASM_LABEL(rei)

ENTRY_NOPROFILE(lev5intr)
        addql   #1,_C_LABEL(intr_depth)
        INTERRUPT_SAVEREG
#include "ser.h"
#if NSER > 0
        jsr     _C_LABEL(ser_fastint)
#else
        INTREQWADDR(%a0)
        movew   #INTF_RBF,%a0@          | clear RBF interrupt in intreq
#endif
        CPUINFO_INCREMENT(CI_NINTR)
        INTERRUPT_RESTOREREG
        addql   #1,_C_LABEL(intrcnt)+20
        subql   #1,_C_LABEL(intr_depth)
        jra     _ASM_LABEL(rei)

#ifdef DRACO
ENTRY_NOPROFILE(DraCoLev2intr)
        addql   #1,_C_LABEL(intr_depth)
        INTERRUPT_SAVEREG

        CIAAADDR(%a0)
        movb    %a0@(CIAICR),%d0        | read irc register (clears ints!)
        jge     Ldrintrcommon           | CIAA IR not set, go through isr chain
        movel   _C_LABEL(draco_intpen),%a0
|       andib   #4,%a0@
|XXX this would better be
        bclr    #2,%a0@
        btst    #0,%d0                  | timerA interrupt?
        jeq     Ldraciaend

        movl    %sp,%sp@-               | push pointer to clockframe
        movw    #PSL_HIGHIPL,%sr        | hardclock at high IPL
        jbsr    _C_LABEL(hardclock)     | call generic clock int routine
        addql   #4,%sp                  | pop params
        addql   #1,_C_LABEL(intrcnt)+32 | add another system clock interrupt

Ldraciaend:
        CPUINFO_INCREMENT(CI_NINTR)
        INTERRUPT_RESTOREREG
        subql   #1,_C_LABEL(intr_depth)
        jra     _ASM_LABEL(rei)

/* XXX on the DraCo rev. 4 or later, lev 1 is vectored here. */
ENTRY_NOPROFILE(DraCoLev1intr)
        addql   #1,_C_LABEL(intr_depth)
        INTERRUPT_SAVEREG
        movl    _C_LABEL(draco_ioct),%a0
        btst    #5,%a0@(7)
        jeq     Ldrintrcommon
        btst    #4,%a0@(7)      | this only happens during autoconfiguration,
        jeq     Ldrintrcommon   | so test last.
        movw    #PSL_HIGHIPL,%sr        | run clock at high ipl
Ldrclockretry:
        movl    %sp,%sp@-               | push pointer to clockframe
        jbsr    _C_LABEL(hardclock)
        addql   #4,%sp                  | pop params
        addql   #1,_C_LABEL(intrcnt)+32 | add another system clock interrupt

        movl    _C_LABEL(draco_ioct),%a0
        tstb    %a0@(9)         | latch timer value
        movw    %a0@(11),%d0    | can't use movpw here, might be 68060
        movb    %a0@(13),%d0
        addw    _C_LABEL(amiga_clk_interval)+2,%d0
        movb    %d0,%a0@(13)    | low byte: latch write value
        movw    %d0,%a0@(11)    | ...and write it into timer
        tstw    %d0             | already positive?
        jcs     Ldrclockretry   | we lost more than one tick, call us again.

        clrb    %a0@(9)         | reset timer irq

        CPUINFO_INCREMENT(CI_NINTR)
        INTERRUPT_RESTOREREG
        subql   #1,_C_LABEL(intr_depth)
        jra     _ASM_LABEL(rei) | XXXX: shouldn't we call the normal lev1?

/* XXX on the DraCo, lev 1, 3, 4, 5 and 6 are vectored here by initcpu() */
ENTRY_NOPROFILE(DraCoIntr)
        addql   #1,_C_LABEL(intr_depth)
        INTERRUPT_SAVEREG
Ldrintrcommon:
        lea     _ASM_LABEL(Drintrcnt)-4,%a0
        movw    %sp@(22),%d0            | use vector offset
        andw    #0xfff,%d0              |   sans frame type
        addql   #1,%a0@(-0x60,%d0:w)    |     to increment apropos counter
        movw    %sr,%sp@-               | push current SR value
        clrw    %sp@-                   |    padded to longword
        jbsr    _C_LABEL(intrhand)      | handle interrupt
        addql   #4,%sp                  | pop SR
        CPUINFO_INCREMENT(CI_NINTR)
        INTERRUPT_RESTOREREG
        subql   #1,_C_LABEL(intr_depth)
        jra     _ASM_LABEL(rei)
#endif


ENTRY_NOPROFILE(lev1intr)
ENTRY_NOPROFILE(lev2intr)
ENTRY_NOPROFILE(lev3intr)
#ifndef LEV6_DEFER
ENTRY_NOPROFILE(lev4intr)
#endif
        addql   #1,_C_LABEL(intr_depth)
        INTERRUPT_SAVEREG
Lintrcommon:
        lea     _C_LABEL(intrcnt),%a0
        movw    %sp@(22),%d0            | use vector offset
        andw    #0xfff,%d0              |   sans frame type
        addql   #1,%a0@(-0x60,%d0:w)    |     to increment apropos counter
        movw    %sr,%sp@-               | push current SR value
        clrw    %sp@-                   |    padded to longword
        jbsr    _C_LABEL(intrhand)      | handle interrupt
        addql   #4,%sp                  | pop SR
        CPUINFO_INCREMENT(CI_NINTR)
        INTERRUPT_RESTOREREG
        subql   #1,_C_LABEL(intr_depth)
        jra     _ASM_LABEL(rei)

/* XXX used to be ifndef DRACO; vector will be overwritten by initcpu() */

ENTRY_NOPROFILE(lev6intr)
#ifdef LEV6_DEFER
        /*
         * cause a level 4 interrupt (AUD3) to occur as soon
         * as we return. Block generation of level 6 ints until
         * we have dealt with this one.
         */
        addql   #1,_C_LABEL(intr_depth)
        moveml  %d0/%a0,%sp@-
        INTREQRADDR(%a0)
        movew   %a0@,%d0
        btst    #INTB_EXTER,%d0
        jeq     Llev6spur
        INTREQWADDR(%a0)
        movew   #INTF_SETCLR+INTF_AUD3,%a0@
        INTENAWADDR(%a0)
        movew   #INTF_EXTER,%a0@
        movew   #INTF_SETCLR+INTF_AUD3,%a0@     | make sure THIS one is ok...
        moveml  %sp@+,%d0/%a0
        subql   #1,_C_LABEL(intr_depth)
        rte
Llev6spur:
        addql   #1,_C_LABEL(intrcnt)+36 | count spurious level 6 interrupts
        moveml  %sp@+,%d0/%a0
        subql   #1,_C_LABEL(intr_depth)
        rte

ENTRY_NOPROFILE(lev4intr)
ENTRY_NOPROFILE(fake_lev6intr)
#endif
        addql   #1,_C_LABEL(intr_depth)
        INTERRUPT_SAVEREG
#ifdef LEV6_DEFER
        /*
         * check for fake level 6
         */
        INTREQRADDR(%a0)
        movew   %a0@,%d0
        btst    #INTB_EXTER,%d0
        jeq     Lintrcommon             | if EXTER not pending, handle normally
#endif

        CIABADDR(%a0)
        movb    %a0@(CIAICR),%d0        | read irc register (clears ints!)
        jge     Lchkexter               | CIAB IR not set, go through isr chain
        INTREQWADDR(%a0)
#ifndef LEV6_DEFER
        movew   #INTF_EXTER,%a0@        | clear EXTER interrupt in intreq
#else
        movew   #INTF_EXTER+INTF_AUD3,%a0@ | clear EXTER & AUD3 in intreq
        INTENAWADDR(%a0)
        movew   #INTF_SETCLR+INTF_EXTER,%a0@ | reenable EXTER interrupts
#endif
        btst    #0,%d0                  | timerA interrupt?
        jeq     Ltstciab4               | no
        movl    %d0,%sp@-               | push CIAB interrupt flags
        lea     %sp@(4),%a1             | get pointer to clockframe
        movl    %a1,%sp@-               | push pointer to clockframe
        jbsr    _C_LABEL(hardclock)     | call generic clock int routine
        addql   #4,%sp                  | pop params
        addql   #1,_C_LABEL(intrcnt)+32 | add another system clock interrupt
        movl    %sp@+,%d0               | pop interrupt flags
Ltstciab4:
#include "fd.h"
#if NFD > 0
        btst    #4,%d0                  | FLG (dskindex) interrupt?
        jeq     Lskipciab               | no
        jbsr    _C_LABEL(fdidxintr)     | tell floppy driver we got it
Lskipciab:
#endif
| other ciab interrupts?
Llev6done:
        CPUINFO_INCREMENT(CI_NINTR)
        INTERRUPT_RESTOREREG
        subql   #1,_C_LABEL(intr_depth)
        jra     _ASM_LABEL(rei)         | all done [can we do rte here?]
Lchkexter:
| check to see if EXTER request is really set?
        movl    _C_LABEL(isr_exter),%a0 | get head of EXTER isr chain
Lnxtexter:
        movl    %a0,%d0                 | test if any more entries
        jeq     Lexterdone              | (spurious interrupt?)
        movl    %a0,%sp@-               | save isr pointer
        movl    %a0@(ISR_ARG),%sp@-
        movl    %a0@(ISR_INTR),%a0
        jsr     %a0@                    | call isr handler
        addql   #4,%sp
        movl    %sp@+,%a0               | restore isr pointer
        movl    %a0@(ISR_FORW),%a0      | get next pointer
        tstl    %d0                     | did handler process the int?
        jeq     Lnxtexter               | no, try next
Lexterdone:
        INTREQWADDR(%a0)
#ifndef LEV6_DEFER
        movew   #INTF_EXTER,%a0@        | clear EXTER interrupt
#else
        movew   #INTF_EXTER+INTF_AUD3,%a0@ | clear EXTER & AUD3 interrupt
        INTENAWADDR(%a0)
        movew   #INTF_SETCLR+INTF_EXTER,%a0@ | reenable EXTER interrupts
#endif
        addql   #1,_C_LABEL(intrcnt)+24 | count EXTER interrupts
        jra     Llev6done
/* XXX endifndef DRACO used to be here */

ENTRY_NOPROFILE(lev7intr)
        addql   #1,_C_LABEL(intrcnt)+28
        /*
         * some amiga zorro2 boards seem to generate spurious NMIs. Best
         * thing to do is to return as quick as possible. That's the
         * reason why I do RTE here instead of jra rei.
         */
        rte                             | all done

/*
 * Kernel access to the current processes kernel stack is via a fixed
 * virtual address.  It is at the same address as in the users VA space.
 */
BSS(esym,4)


/*
 * Initialization
 *
 * A5 contains physical load point from boot
 * exceptions vector thru our table, that's bad.. just hope nothing exceptional
 * happens till we had time to initialize ourselves..
 */
BSS(lowram,4)

#define RELOC(var, ar)                  \
        lea     _C_LABEL(var),ar;       \
        addl    %a5,ar

#define ASRELOC(var, ar)                \
        lea     _ASM_LABEL(var),ar;     \
        addl    %a5,ar

        .text

        | XXX should be a symbol?
        | 2: needs a4 = esym
        | 3: no chipmem requirement
        |    bootinfo data structure

        .word   0
        .word   0x0003                  | loadbsd version required
ASENTRY_NOPROFILE(start)
        lea     %pc@(L_base),%a5        | initialize relocation register

        movw    #PSL_HIGHIPL,%sr        | no interrupts
        ASRELOC(tmpstk,%a6)
        movl    %a6,%sp                 | give ourselves a temporary stack

        | save the passed parameters. "prepass" them on the stack for
        | later catch by start_c()
        movl    %a5,%sp@-               | pass loadbase
        movl    %d6,%sp@-               | pass boot partition offset
        movl    %a2,%sp@-               | pass sync inhibit flags
        movl    %d3,%sp@-               | pass AGA mode
        movl    %a4,%sp@-               | pass address of _esym
        movl    %d1,%sp@-               | pass chipmem-size
        movl    %d0,%sp@-               | pass fastmem-size
        movl    %a0,%sp@-               | pass fastmem_start
        movl    %d5,%sp@-               | pass machine id

        /*
         * initialize some hw addresses to their physical address
         * for early running
         */
#ifdef DRACO
        /*
         * this is already dynamically done on DraCo
         */
        cmpb    #0x7D,%sp@
        jne     LisAmiga1
| debug code:
| we should need about 1 uSec for the loop.
| we dont need the AGA mode register.
        movel   #100000,%d3
LisDraco0:
#ifdef DEBUG_KERNEL_START
        movb    #0,0x200003c8
        movb    #00,0x200003c9
        movb    #40,0x200003c9
        movb    #00,0x200003c9
|XXX:
        movb    #0,0x200003c8
        movb    #40,0x200003c9
        movb    #00,0x200003c9
        movb    #00,0x200003c9
        subql   #1,%d3
        jcc     LisDraco0
#endif

        RELOC(chipmem_start, %a0)
        movl    #0,%a0@

        RELOC(CIAAbase, %a0)
        movl    #0x2801001, %a0@
        RELOC(CIABbase, %a0)
        movl    #0x2800000, %a0@

        /* XXXX more to come here; as we need it */

        jra     LisDraco1
LisAmiga1:
#endif
        RELOC(chipmem_start, %a0)
        movl    #0x400,%a0@
        RELOC(CIAAbase, %a0)
        movl    #0xbfe001,%a0@
        RELOC(CIABbase, %a0)
        movl    #0xbfd000,%a0@
        RELOC(CUSTOMbase, %a0)
        movl    #0xdff000,%a0@

#ifdef DRACO
LisDraco1:
#endif
        /*
         * initialize the timer frequency
         */
        RELOC(eclockfreq, %a0)
        movl    %d4,%a0@

        movl    #AMIGA_68030,%d1        | 68030 Attn flag from exec
        andl    %d5,%d1
        jeq     Ltestfor020
        RELOC(mmutype, %a0)
        movl    #MMU_68030,%a0@         | assume 020 means 851
        RELOC(cputype, %a0)
        movl    #CPU_68030,%a0@
        jra     Lsetcpu040              | skip to init.
Ltestfor020:
        movl    #AMIGA_68020,%d1        | 68020 Attn flag from exec
        andl    %d5,%d1
        jeq     Lsetcpu040
        RELOC(mmutype, %a0)
        movl    #MMU_68851,%a0@
        RELOC(cputype, %a0)
        movl    #CPU_68020,%a0@
Lsetcpu040:
        movl    #CACHE_OFF,%d0          | 68020/030 cache
        movl    #AMIGA_68040,%d1
        andl    %d1,%d5
        jeq     Lstartnot040            | it is not 68040
        RELOC(mmutype, %a0)
        movl    #MMU_68040,%a0@         | same as hp300 for compat
        RELOC(cputype, %a0)
        movl    #CPU_68040,%a0@
        .word   0xf4f8                  | cpusha bc - push and invalidate caches
        movl    #CACHE40_OFF,%d0        | 68040 cache disable
#ifndef BB060STUPIDROM
        btst    #7,%sp@(3)
        jeq     Lstartnot040
        movl    #CPU_68060,%a0@         | and in the cputype
        orl     #IC60_CABC,%d0          | XXX and clear all 060 branch cache
#else
        movc    %d0,%cacr
        bset    #30,%d0                 | not allocate data cache bit
        movc    %d0,%cacr               | does it stick?
        movc    %cacr,%d0
        tstl    %d0
        jeq     Lstartnot040
        bset    #7,%sp@(3)              | note it is '60 family in machineid
        movl    #CPU_68060,%a0@         | and in the cputype
        orl     #IC60_CABC,%d0          | XXX and clear all 060 branch cache
        .word   0x4e7a,0x1808           | movc  pcr,d1
        swap    %d1
        cmpw    #0x430,%d1
        jne     Lstartnot040            | but no FPU
        bset    #6,%sp@(3)              | yes, we have FPU, note that
        swap    %d1
        bclr    #1,%d1                  | ... and switch it on.
        .word   0x4e7b,0x1808           | movc  d1,pcr
#endif
Lstartnot040:
        movc    %d0,%cacr               | clear and disable on-chip cache(s)
        movl    #_C_LABEL(vectab),%a0
        movc    %a0,%vbr

/* let the C function initialize everything */
        RELOC(start_c, %a0)
        jbsr    %a0@
        lea     %sp@(4*9),%sp

#ifdef DRACO
        RELOC(machineid,%a0)
        cmpb    #0x7d,%a0@
        jne     LAmiga_enable_MMU

        lea     %pc@(0),%a0
        movl    %a0,%d0
        andl    #0xff000000,%d0
        orl     #0x0000c044,%d0         | 16 MB, ro, cache inhibited
        .word   0x4e7b,0x0004           | movc %d0,%itt0
        .word   0xf518                  | pflusha
        movl    #MMU40_TCR_BITS,%d0     | enable MMU
        .word   0x4e7b,0x0003           | movc %d0,%tc
        jmp     Lcleanitt0:l
Lcleanitt0:
        movq    #0,%d0
        .word   0x4e7b,0x0004           | movc %d0,%itt0
        bra     LMMUenable_end

LAmiga_enable_MMU:
#endif /* DRACO */

/* Copy just the code to enable the MMU into chip memory */
        lea     LMMUenable_start,%a0
        movl    #LMMUenable_start:l,%a1
        lea     LMMUenable_end,%a2
Lcopy_MMU_enabler:
        movl    %a0@+,%a1@+
        cmpl    %a0,%a2
        jgt     Lcopy_MMU_enabler

        jmp     LMMUenable_start:l

LMMUenable_start:

/* enable the MMU */
#if defined(M68040) || defined(M68060)
        RELOC(mmutype, %a0)
        cmpl    #MMU_68040,%a0@
        jne     Lenable030
        .word   0xf518                  | pflusha
        movl    #MMU40_TCR_BITS,%d0     | enable MMU
        .word   0x4e7b,0x0003           | movc  %d0,%tc
        jmp     LMMUenable_end:l
#endif /* M68040 || M68060 */
Lenable030:
        pflusha
        lea     Ltc,%a0
        pmove   %a0@,%tc
        jmp     LMMUenable_end:l

Ltc:    .long   MMU51_TCR_BITS          | see pmap.h

LMMUenable_end:
        lea     _ASM_LABEL(tmpstk),%sp  | give ourselves a temporary stack
        jbsr    _C_LABEL(vec_init)      | initialize the vector table

        /* start_c_finish() returns lwp0 SP in %a0 */
        jbsr    _C_LABEL(start_c_finish)
        movl    %a0,%sp                 | now running on lwp0's stack
        movl    #0,%a6                  | terminate the stack back trace

/* flush TLB and turn on caches */
        jbsr    _C_LABEL(_TBIA)         | invalidate TLB
        movl    #CACHE_ON,%d0
        tstl    %d5
        jeq     Lcacheon
| is this needed? MLH
        .word   0xf4f8                  | cpusha bc - push & invalidate caches
        movl    #CACHE40_ON,%d0
#ifdef M68060
        cmpl    #CPU_68060,_C_LABEL(cputype)
        jne     Lcacheon
        movl    #PCR_ESS,%d0            | enable superscalar dispatch
        .long   0x4e7b0808              | movc %d0,%pcr
        movl    #CACHE60_ON,%d0
#endif
Lcacheon:
        movc    %d0,%cacr               | clear cache(s)
/* final setup for C code */

        movw    #PSL_LOWIPL,%sr         | lower SPL

        movl    %d7,_C_LABEL(boothowto) | save reboot flags

        jra     _C_LABEL(main)          | main() (never returns)

/*
 * Primitives
 */

/*
 * non-local gotos
 */
ENTRY(qsetjmp)
        movl    %sp@(4),%a0     | savearea pointer
        lea     %a0@(40),%a0    | skip regs we do not save
        movl    %a6,%a0@+       | save FP
        movl    %sp,%a0@+       | save SP
        movl    %sp@,%a0@       | and return address
        moveq   #0,%d0          | return 0
        rts

/*
 * Check out a virtual address to see if it's okay to write to.
 *
 * probeva(va, fc)
 *
 */
ENTRY(probeva)
        movl    %sp@(8),%d0
        movec   %d0,%dfc
        movl    %sp@(4),%a0
        .word   0xf548                          | ptestw (a0)
        moveq   #FC_USERD,%d0                   | restore DFC to user space
        movc    %d0,%dfc
        .word   0x4e7a,0x0805                   | movec  MMUSR,d0
        rts

/*
 * Handle the nitty-gritty of rebooting the machine.
 *
 */
#if defined(P5PPC68KBOARD)
        .data
GLOBAL(p5ppc)
        .long   0
        .text
#endif

ENTRY_NOPROFILE(machine_reboot)
        movl    #CACHE_OFF,%d0
        cmpl    #MMU_68040,_C_LABEL(mmutype)    | is it 68040
        jne     Ldoboot0
        .word   0xf4f8                  | cpusha bc - push and invalidate caches
        nop
        movl    #CACHE40_OFF,%d0
Ldoboot0:
        movc    %d0,%cacr                       | disable on-chip cache(s)

        movw    #0x2700,%sr                     | cut off any interrupts

#if defined(P5PPC68KBOARD)
        tstl    _C_LABEL(p5ppc)
        jne     Lp5ppcboot
#endif
#if defined(DRACO)
        cmpb    #0x7d,_C_LABEL(machineid)
        jeq     LdbOnDraCo
#endif

        | clear first 4k of CHIPMEM
        movl    _C_LABEL(CHIPMEMADDR),%a0
        movl    %a0,%a1
        movl    #1024,%d0
Ldb1:
        clrl    %a0@+
        dbra    %d0,Ldb1

        | now, copy the following code over
|       lea     %a1@(Ldoreboot),%a0     | KVA starts at 0, CHIPMEM is phys 0
|       lea     %a1@(Ldorebootend),%a1
|       lea     %pc@(Ldoreboot-.+2),%a0
|       addl    %a1,%a0
|       lea     %a0@(128),%a1
|       lea     %pc@(Ldoreboot-.+2),%a2
        lea     Ldoreboot,%a2
        lea     Ldorebootend,%a0
        addl    %a1,%a0
        addl    %a2,%a1
        exg     %a0,%a1
Ldb2:
        movel   %a2@+,%a0@+
        cmpl    %a1,%a0
        jle     Ldb2

        | ok, turn off MMU..
Ldoreboot:
        cmpl    #MMU_68040,_C_LABEL(mmutype)    | is it 68040
        jeq     Lmmuoff040
        lea     _ASM_LABEL(zero),%a0
        pmove   %a0@,%tc                | Turn off MMU
        lea     _ASM_LABEL(nullrp),%a0
        pmove   %a0@,%crp               | Turn off MMU some more
        pmove   %a0@,%srp               | Really, really, turn off MMU
        jra     Ldoboot1
Lmmuoff040:
        movl    #0,%d0
        .word   0x4e7b,0x0003           | movc d0,TC
        .word   0x4e7b,0x0806           | movc d0,URP
        .word   0x4e7b,0x0807           | movc d0,SRP
Ldoboot1:

        | this weird code is the OFFICIAL way to reboot an Amiga ! ..
        lea     0x1000000,%a0
        subl    %a0@(-0x14),%a0
        movl    %a0@(4),%a0
        subl    #2,%a0
        cmpw    #0x4e70,%a0@            | 68040 kludge: if ROM entry is not
        jne     Ldoreset                | a reset, do the reset here
        jmp     %a0@                    | otherwise, jump to the ROM to reset
        | reset needs to be on longword boundary
        nop
#ifdef __ELF__
        .align  4
#else
        .align  2
#endif
Ldoreset:
        | reset unconfigures all memory!
        reset
        | now rely on prefetch for next jmp
        jmp     %a0@
        | NOT REACHED

#if defined(P5PPC68KBOARD)
Lp5ppcboot:
| The Linux-Apus boot code does it in a similar way
| For 040 on uncached pages, eieio can be replaced by nothing.
        movl    _C_LABEL(ZTWOROMADDR),%a0
        lea     %a0@(0xf60000-0xd80000),%a0
        movb    #0x60,%a0@(0x20)
        movb    #0x50,%a0@(0x20)
        movb    #0x30,%a0@(0x20)
        movb    #0x40,%a0@(0x18)
        movb    #0x04,%a0@
Lwaithere:
        jra     Lwaithere
#endif

#ifdef DRACO
LdbOnDraCo:
| we use a TTR. We want to boot even if half of us is already dead.

        movl    _C_LABEL(boot_fphystart), %d0
        lea     LdoDraCoBoot, %a0
        lea     %a0@(%d0),%a0
        andl    #0xFF000000,%d0
        orl     #0x0000C044,%d0 | enable, supervisor, CI, RO
        .word   0x4e7b,0x0004   | movc d0,ITT0
        jmp     %a0@

#ifdef __ELF__
        .align  4
#else
        .align  2
#endif
LdoDraCoBoot:
| turn off MMU now ... were more ore less guaranteed to run on 040/060:
        movl    #0,%d0
        .word   0x4e7b,0x0003   | movc d0,TC
        .word   0x4e7b,0x0806   | movc d0,URP
        .word   0x4e7b,0x0807   | movc d0,SRP
        .word   0x4e7b,0x0004   | movc d0,ITT0
        nop
| map in boot ROM @0:
        reset
| and simulate what a reset exception would have done.
        movl    4,%a0
        movl    0,%a7
        jmp     %a0@
        | NOT REACHED
#endif
/*
 * Reboot directly into a new kernel image.
 * kernel_reload(image, image_size, entry,
 *               fastram_start, fastram_size, chipram_start, esym, eclockfreq)
 */
ENTRY_NOPROFILE(kernel_reload)
        lea     Lreload_copy,%a0        | cursory validity check of new kernel
        movl    %a0@,%d0                |  to see if the kernel reload code
        addl    %sp@(4),%a0             |  in new image matches running kernel
        cmpl    %a0@,%d0
        jeq     Lreload_ok
        rts                             | It doesn't match - can't reload
Lreload_ok:
        jsr     _C_LABEL(bootsync)
        CUSTOMADDR(%a5)

        movew   #(1<<9),%a5@(0x096)     | disable DMA (before clobbering chipmem)

        movl    #CACHE_OFF,%d0
        cmpl    #MMU_68040,_C_LABEL(mmutype)
        jne     Lreload1
        .word   0xf4f8          | cpusha bc - push and invalidate caches
        nop
        movl    #CACHE40_OFF,%d0
Lreload1:
        movc    %d0,%cacr               | disable on-chip cache(s)

        movw    #0x2700,%sr             | cut off any interrupts
        movel   _C_LABEL(boothowto),%d7 | save boothowto
        movel   _C_LABEL(machineid),%d5 | (and machineid)

        movel   %sp@(16),%a0            | load memory parameters
        movel   %sp@(20),%d0
        movel   %sp@(24),%d1
        movel   %sp@(28),%a4            | esym
        movel   %sp@(32),%d4            | eclockfreq
        movel   %sp@(36),%d3            | AGA mode
        movel   %sp@(40),%a2            | sync inhibit flags
        movel   %sp@(44),%d6            | boot partition offset

        movel   %sp@(12),%a6            | find entrypoint (a6)

        movel   %sp@(4),%a1             | copy kernel to low chip memory
        movel   %sp@(8),%d2
        movl    _C_LABEL(CHIPMEMADDR),%a3
Lreload_copy:
        movel   %a1@+,%a3@+
        subl    #4,%d2
        jcc     Lreload_copy

        | ok, turn off MMU..
        cmpl    #MMU_68040,_C_LABEL(mmutype)
        jeq     Lreload040
        lea     _ASM_LABEL(zero),%a3
        pmove   %a3@,%tc                | Turn off MMU
        lea     _ASM_LABEL(nullrp),%a3
        pmove   %a3@,%crp               | Turn off MMU some more
        pmove   %a3@,%srp               | Really, really, turn off MMU
        jra     Lreload2
Lreload040:
        movl    #0,%d2
        .word   0x4e7b,0x2003   | movc d2,TC
        .word   0x4e7b,0x2806   | movc d2,URP
        .word   0x4e7b,0x2807   | movc d2,SRP
Lreload2:

        moveq   #0,%d2                  | clear unused registers
        subl    %a1,%a1
        subl    %a3,%a3
        subl    %a5,%a5
        jmp     %a6@                    | start new kernel


| A do-nothing MMU root pointer (includes the following long as well)

ASLOCAL(nullrp)
        .long   0x7fff0001
ASLOCAL(zero)
        .long   0
Ldorebootend:

        .data
        .space  PAGE_SIZE
        .align  4
ASGLOBAL(tmpstk)

GLOBAL(mmutype)
        .long   MMU_68851
GLOBAL(cputype)
        .long   CPU_68020
GLOBAL(ectype)
        .long   EC_NONE
GLOBAL(delay_divisor)
        .long   12              | should be enough for 80 MHz 68060
                                | will be adapted to other CPUs in
                                | start_c_cleanup and calibrated
                                | at clock attach time.
#ifdef DEBUG
ASGLOBAL(fulltflush)
        .long   0
ASGLOBAL(fullcflush)
        .long   0
ASGLOBAL(timebomb)
        .long   0
#endif
/* interrupt counters */
GLOBAL(intrnames)
        .asciz  "spur"          | spurious interrupt
        .asciz  "tbe/soft"      | serial TBE & software
        .asciz  "kbd/ports"     | keyboard & PORTS
        .asciz  "vbl"           | vertical blank
        .asciz  "audio"         | audio channels
        .asciz  "rbf"           | serial receive
        .asciz  "exter"         | EXTERN
        .asciz  "nmi"           | non-maskable
        .asciz  "clock"         | clock interrupts
        .asciz  "spur6"         | spurious level 6
#ifdef DRACO
        .asciz  "kbd/soft"      | 1: native keyboard, soft ints
        .asciz  "cia/zbus"      | 2: cia, PORTS
        .asciz  "lclbus"        | 3: local bus, e.g. Altais vbl
        .asciz  "drscsi"        | 4: mainboard scsi
        .asciz  "superio"       | 5: superio chip
        .asciz  "lcl/zbus"      | 6: lcl/zorro lev6
        .asciz  "buserr"        | 7: nmi: bus timeout
#endif
GLOBAL(eintrnames)
#ifdef __ELF__
        .align  4
#else
        .align  2
#endif
GLOBAL(intrcnt)
        .long   0,0,0,0,0,0,0,0,0,0
#ifdef DRACO
ASLOCAL(Drintrcnt)
        .long   0,0,0,0,0,0,0
#endif
GLOBAL(eintrcnt)