Atari ST Book: POWER.PRG listing

Commented assembly listing for the POWER.PRG in an Atari ST-Book. Also available on GitHub.

main:       pea       ($0000).w
            move.l    #'_PWR',-(sp)
            jsr       (_getcookie).l            ; _PWR cookie installed?
            addq.l    #8,sp
            tst.w     d0
            beq       install                   ; (no -> install app)
_term:      clr.w     -(sp)
            trap      #1                        ; Pterm0()
            illegal

install:    move.l    #infoText,-(sp)
            move.w    #9,-(sp)
            trap      #1                        ; Cconws()
            addq.w    #6,sp

            pea       (cookiePtr).l
            pea       ($0000).w
            jsr       (_getcookie).l
            addq.l    #8,sp
            addq.l    #8,(cookiePtr).l

            movea.l   4(sp),a0                  ; BASEPAGE
            move.l    (cookiePtr).l,d0
            lsl.l     #3,d0
            move.l    $18(a0),d1                ; Pointer to beginning of BSS segment
            add.l     $1c(a0),d1                ; + BSS segment size
            sub.l     a0,d1                     ; - BASEPAGE address = size of the app memory
            add.l     d1,d0
            move.l    (cookiePtr).l,-(sp)
            move.l    d0,(cookiePtr).l
            move.l    a0,-(sp)
            move.l    #cookie_blankScreenTimer,-(sp)
            move.l    #'_PWR',-(sp)
            jsr       (setCookie).l
            adda.l    #$10,sp
            tst.l     d0
            bmi.s     _term
            bpl       install2
            subi.l    #$40,(cookiePtr).l
            clr.b     (v_opnwk_called).l

install2:   clr.l     -(sp)
            move.w    #$20,-(sp)
            trap      #1                        ; oldval = Super(0L)
            addq.w    #6,sp
            move.l    d0,-(sp)
            move.w    #$20,-(sp)                ; Super(oldval)

            jsr       (setupVectors).l

            move.b    #0,(ahdiDetected).l       ; AHDI check
            movea.l   (pun_ptr).w,a0
            cmpi.l    #'AHDI',$52(a0)           ; P_cookie in PUN_INFO in the AHDI driver
            bne       install3
            lea       $52(a0),a1
            cmpa.l    $56(a0),a1                ; check P_cookptr in the AHDI driver
            bne       install3
            cmpi.w    #$500,$5a(a0)             ; check P_version in the AHDI driver
            blt       install3
            move.b    #1,(ahdiDetected).l
            move.b    $63(a0),(ahdiStandbyTime).l ;idle time limit to spin down IDE unit 0

install3:   move.l    #powerAlarm,(mfp_MonochromeDetect).w ;power alarm
            bclr      #7,(aer).w                ; clear pending "Monochrome Monitor Detect"
            bset      #7,(iera).w               ; enable "Monochrome Monitor Detect" interrupt
            bset      #7,(imra).w               ; enable "Monochrome Monitor Detect" interrupt mask
            move.l    #powerAlarm,(Level7IRQ).w ; power fail

            move.w    #3,(countdownVBLTimer).l  ; reset VBL timer
            bclr      #0,(synmod+1).l           ; enable combo -> shadow controller video transfer
            move.l    (cookie_blankScreenTimer).l,(blankScreenTimerCounter).l
            bclr      #3,(lcdPowerControlShadow).w ;LCD on
            move.b    (lcdPowerControlShadow).w,(lcdPowerControl).l
            move.l    (cookie_sleepTimer).l,(sleepTimerCounter).l

            trap      #1                        ; Super(oldval)
            addq.w    #6,sp
            clr.w     -(sp)
            move.l    (cookiePtr).l,-(sp)
            move.w    #$31,-(sp)
            trap      #1                        ; Ptermres()
            illegal

***************************************************************************
*
***************************************************************************
_getcookie: movem.l   d6-d7/a6,-(sp)
            move.l    $10(sp),d6                ; cookie
            movea.l   $14(sp),a6                ; ptr to the cookie value, if found
            clr.l     d7
            move.l    #1,-(sp)
            move.w    #$20,-(sp)
            trap      #1
            tst.l     d0
            bne       _getcookies
            move.l    #0,2(sp)
            move.w    #$20,(sp)
            trap      #1
            move.l    d0,d7
_getcookies:addq.l    #6,sp
            movea.l   (_p_cookies).w,a0
            move.l    a0,d0
            beq       _getcookiee
_getcookiel:move.l    (a0),d0
            cmp.l     d0,d6
            beq       _getcookie2
            tst.l     d0
            beq       _getcookiee
            addq.l    #8,a0
            bra.s     _getcookiel
_getcookie2:move.l    a6,d0
            beq       _getcookie3
            move.l    4(a0),(a6)
_getcookie3:moveq     #1,d0
_getcookiee:move.l    d0,d6
            tst.l     d7
            beq       _getcookiex
            move.l    d7,-(sp)
            move.w    #$20,-(sp)
            trap      #1
            addq.l    #6,sp
_getcookiex:move.l    d6,d0
            movem.l   (sp)+,d6-d7/a6
            rts

***************************************************************************
*
***************************************************************************
setCookie:  link      a6,#0
            move.l    d7,-(sp)
            moveq     #0,d7
            move.l    #1,-(sp)
            move.w    #$20,-(sp)
            trap      #1
            tst.l     d0
            bne       setCookies
            move.l    #0,2(sp)
            move.w    #$20,(sp)
            trap      #1
            move.l    d0,d7
setCookies: addq.l    #6,sp
            movea.l   (_p_cookies).w,a0
            move.l    a0,d0
            beq       setCookiee
            moveq     #0,d0
setCookiel: addq.l    #1,d0
            tst.l     (a0)
            beq       setCookie2
            addq.l    #8,a0
            bra.s     setCookiel
setCookie2: cmp.l     4(a0),d0
            beq       setCookiee2
            move.l    4(a0),$c(a0)
            clr.l     8(a0)
            move.l    8(a6),(a0)
            move.l    $c(a6),4(a0)
            moveq     #0,d0
            bra       setCookiee5
setCookiee: cmpi.l    #2,$14(a6)
            blt       setCookiee4
            move.l    (resvector).w,(oldcookieresvector).l
            move.l    (resvalid).w,(oldcookieresvalid).l
            move.l    #cookieResetVector,(resvector).w
            move.l    #$31415926,(resvalid).w
            movea.l   $10(a6),a0
            move.l    a0,(_p_cookies).w
            move.l    8(a6),(a0)+
            move.l    $c(a6),(a0)+
            clr.l     (a0)+
            move.l    $14(a6),(a0)
            moveq     #1,d0
            bra       setCookiee5
cookieResetVector:clr.l (_p_cookies).w
            move.l    (oldcookieresvalid).l,(resvalid).w
            move.l    (oldcookieresvector).l,(resvector).w
            jmp       (a6)

setCookiee2:cmp.l     $14(a6),d0
            ble       setCookiee4
            move.l    d0,d1
            subq.l    #2,d1
            movea.l   (_p_cookies).w,a0
            movea.l   $10(a6),a1
            move.l    a1,(_p_cookies).w
setCookiee3:move.l    (a0)+,(a1)+
            move.l    (a0)+,(a1)+
            dbra      d1,setCookiee3
            move.l    8(a6),(a1)+
            move.l    $c(a6),(a1)+
            clr.l     (a1)+
            move.l    $14(a6),(a1)
            moveq     #1,d0
            bra       setCookiee5
setCookiee4:moveq     #$ff,d0
setCookiee5:tst.l     d7
            beq       setCookiex
            move.l    d0,8(a6)
            move.l    d7,-(sp)
            move.w    #$20,-(sp)
            trap      #1
            addq.l    #6,sp
            move.l    8(a6),d0
setCookiex: move.l    (sp)+,d7
            unlk      a6
            rts

***************************************************************************
*       reset vector - used to wake up the machine back                   *
***************************************************************************
resetVector:move.l    (oldresvalid).l,(resvalid).w
            move.l    (oldresvector).l,(resvector).w
            bclr      #2,(lcdPowerControlShadow).w ;power is on
            bclr      #3,(lcdPowerControlShadow).w ;LCD on
            move.b    (lcdPowerControlShadow).w,(lcdPowerControl).l
            movea.l   (oldUSP).l,a0
            move      a0,usp
            movea.l   (oldSSP).l,sp
            clr.w     (saveOrRestoreFlag).l     ; Restore chip registers

            subi.l    #$2e,(savptr).w
            bsr       saveState_video
            bsr       saveState_halftone
            bsr       saveState_MFP
            bsr       saveState_MIDI
            bsr       saveState_PSG
            bsr       saveState_time
            bsr       saveState_Keyboard
            bsr       saveState_AHDI
            move.l    #0,-(sp)
            move.w    #$2f,-(sp)
            trap      #$e                       ; Waketime(0L) - clear Waketime
            addq.w    #6,sp
            addi.l    #$2e,(savptr).w

            bset      #7,(imra).w
            andi.b    #$f,(tcdcr).w
            move.b    #$df,(iprb).w
            movea.l   (etv_timer).w,a6
            subq.l    #2,sp
            move.w    #511-1,d7
resetVector2:clr.w    (sp)
            jsr       (a6)
            dbra      d7,resetVector2
            addq.l    #2,sp
            ori.b     #$50,(tcdcr).w

resetVector3:move.w   #30,d1
resetVector4:move.b   (STConfig+1).l,d0
            cmp.b     #$fe,d0                   ; Power button still pressed?
            beq.s     resetVector3              ; yes -> continue to wait
            cmp.b     #$ff,d0                   ; no pressed?
            bne       resetVector5              ; -> done
            btst      #7,(gpip).w               ; power alarm IRQ pending?
            beq.s     resetVector3              ; -> continue to wait
            btst      #5,(iprb).w
            beq.s     resetVector4
            move.b    #$df,(iprb).w
            dbra      d1,resetVector4

resetVector5:move.b   #$7f,(ipra).w
            movem.l   (saveRegs).l,d0-d7/a0-a6
            rte

***************************************************************************
*       NMI or MFP IRQ #15                                                *
***************************************************************************
powerAlarm: btst      #2,(STConfig+1).l         ; wait for RTC alarm to be off
            bne       powerAlarm2
            bclr      #2,(rtc_mode+1).l         ; disable RTC alarm
            bra.s     powerAlarm

powerAlarm2:movem.l   d0-d2,(saveRegs).l
            move.b    (STConfig+1).l,d0         ; IRQ source
            move.b    d0,d1
            and.b     #$a4,d1
            cmp.b     #$a4,d1                   ; _only_ expansion wake, modem wake or RTC wake?
            bne       powerAlarmExit            ; these are always ignored ->
            btst      #3,d0                     ; power dead?
            beq       gotoSleep                 ; -> force sleep
            btst      #0,d0                     ; power switch?
            beq       gotoSleep                 ; -> force sleep
            btst      #1,d0                     ; top closed?
            beq       gotoSleep                 ; -> force sleep
            cmp.b     #$ff,d0                   ; anything else?
            bne       powerAlarmExit            ; -> ignore it

* we got no IRQ source, wait for a bit to see if that changes
            move.w    #$1e,d1                   ; 31 scan lines
            move      sr,d2
            move      #$2700,sr
powerAlarml:move.b    (STConfig+1).l,d0         ; read the IRQ source
            cmp.b     #$ff,d0                   ; anything changed?
            bne       gotoSleep                 ; -> force sleep
            btst      #5,(iprb).w               ; scan line IRQ?
            beq.s     powerAlarml               ; no -> continue waiting
            move.b    #$df,(iprb).w             ; confirm IRQ
            dbra      d1,powerAlarml            ; wait 31 scan lines ->
            move      d2,sr
powerAlarmExit:movem.l (saveRegs).l,d0-d2
            move.b    #$7f,(isra).w             ; re-enable MFP IRQ #15
            rte                                 ; continue normally

***************************************************************************
*       let the system go to sleep                                        *
***************************************************************************
gotoSleep:  move      #$2700,sr                 ; stop all interrupts
            movem.l   d3-d7/a0-a6,(saveRegs+4*3).l ;save all remaining registers
            btst      #3,(STConfig+1).l         ; power dead?
            beq       shutdownPower             ; -> force shutdown

            subi.l    #$2e,(savptr).w           ; decrement re-entrancy stack for xbios
            move.l    #1,-(sp)
            move.w    #$2f,-(sp)
            trap      #$e                       ; Waketime(1L) - enable Waketime
            addq.w    #6,sp
            addi.l    #$2e,(savptr).w           ; re-increment re-entrancy stack for xbios
            tst.l     d0
            beq       shutdownPower

            move.b    #$c,(ide_seccn).l
            move.b    #$e2,(ide_comst).l        ; IDE standby command
            addi.l    #11*50,(blankScreenTimerCounter).l    ; wait for 11s
            addi.l    #11*50,(sleepTimerCounter).l
            movea.l   (etv_timer).w,a6
            subq.l    #2,sp
            move.w    #511-1,d7
gotoSleep2: clr.w     (sp)                      ; calling etv_timer as ~10s have passed
            jsr       (a6)
            dbra      d7,gotoSleep2
            addq.l    #2,sp

gotoSleep3: move.b    #30,d0
gotoSleep4: btst      #7,(gpip).w
            beq.s     gotoSleep3
            btst      #5,(iprb).w
            beq.s     gotoSleep4
            move.b    #$df,(iprb).w
            dbra      d0,gotoSleep4
            move.b    #$7f,(isra).w
            movem.l   (saveRegs).l,d0-d7/a0-a6
            rte

***************************************************************************
*       shut the system down because of a power failure                   *
***************************************************************************
shutdownPower:move    usp,a0
            move.l    a0,(oldUSP).l
            move.l    sp,(oldSSP).l
            move.w    #1,(saveOrRestoreFlag).l  ; Save chip registers
            bclr      #7,(imra).w
            bset      #3,(lcdPowerControlShadow).w ;LCD off
            move.b    (lcdPowerControlShadow).w,(lcdPowerControl).l
            subi.l    #$2e,(savptr).w
            bsr       saveState_video
            bsr       saveState_halftone
            bsr       saveState_FDC
            bsr       saveState_bios_rwabs
            bsr       saveState_AHDI
            bsr       saveState_PSG
            bsr       saveState_MFP
            addi.l    #$2e,(savptr).w
            move.l    (resvalid).w,(oldresvalid).l
            move.l    (resvector).w,(oldresvector).l
            move.l    #$31415926,(resvalid).w
            move.l    #resetVector,(resvector).w
            move.b    (lcdPowerControlShadow).w,d0
            bset      #4,d0                     ; REFRESH_MACHINE output
            move.b    d0,(lcdPowerControl).l
            bset      #0,(synmod+1).l           ; disable combo -> shadow controller video transfer
debouncel:  move.w    #100,d0                   ; wait for up to 500ms for the power button to be released
            clr.b     (tacr).w                  ; stop timer a
            bclr      #5,(iera).w               ; disable timer a
            bclr      #5,(imra).w               ; clear pending timer a interrupts
            bset      #5,(iera).w               ; enable timer a
            move.b    #$c0,(tadr).w
            move.b    #5,(tacr).w               ; timer a at 5ms
debouncel2: cmpi.b    #$fe,(STConfig+1).l       ; Power button still pressed?
            beq.s     debouncel
            btst      #5,(ipra).w               ; timer a not triggered
            beq.s     debouncel2
            move.b    #$df,(ipra).w             ; clear timer a interrupt
            dbra      d0,debouncel2             ; continue waiting
            bset      #2,(lcdPowerControlShadow).w ;power off
            move.b    (lcdPowerControlShadow).w,d0
            bset      #4,d0                     ; REFRESH_MACHINE output
            move.b    d0,(lcdPowerControl).l
* The following opcode is pre-fetched, the CPU doesn't need to touch this memory after the previous opcode anymore
endlessLoop:bra.s     endlessLoop               ; wakeup via reset!
            trap      #1                        ; this is useless stuff...
            addq.w    #6,sp
            illegal

***************************************************************************
*   save/restore the XBIOS time
*   Force reading the time from the RTC to update the GEMDOS time after sleep
***************************************************************************
saveState_time:tst.w  (saveOrRestoreFlag).l
            bne       saveState_timee
            move.w    #$17,-(sp)
            trap      #$e                       ; Gettime()
            addq.w    #2,sp
saveState_timee:rts

***************************************************************************
*   save/restore the MFP state
***************************************************************************
saveState_MFP:lea     (sav_MFP).l,a0
            tst.w     (saveOrRestoreFlag).l
            beq       saveState_MFPe
            move.b    (aer).w,(a0)+
            move.b    (ddr).w,(a0)+
            move.b    (iera).w,(a0)+
            move.b    (ierb).w,(a0)+
            move.b    (imra).w,(a0)+
            move.b    (imrb).w,(a0)+
            move.b    (vr).w,(a0)+
            move.b    (tacr).w,(a0)+
            move.b    (tbcr).w,(a0)+
            move.b    (tcdcr).w,(a0)+
            move.b    (scr).w,(a0)+
            move.b    (ucr).w,(a0)+
            move.b    (rsr).w,(a0)+
            move.b    (tsr).w,(a0)+
            move.b    #$c0,(sav_MFP+16).l
            lea       (tadr).w,a0
            lea       (tacr).w,a1
            bsr       saveState_MFPsave
            move.b    d0,(sav_MFP+14).l
            lea       (tbdr).w,a0
            lea       (tbcr).w,a1
            bsr       saveState_MFPsave
            move.b    d0,(sav_MFP+15).l
            lea       (tddr).w,a0
            lea       (tcdcr).w,a1
            bsr       saveState_MFPsave
            move.b    d0,(sav_MFP+17).l
            rts

saveState_MFPe:move.b (a0)+,(aer).w
            move.b    (a0)+,(ddr).w
            move.b    (a0)+,(iera).w
            move.b    (a0)+,(ierb).w
            move.b    (a0)+,(imra).w
            move.b    (a0)+,(imrb).w
            move.b    (a0)+,(vr).w
            move.b    (a0)+,(tacr).w
            move.b    (a0)+,(tbcr).w
            move.b    (a0)+,(tcdcr).w
            move.b    (a0)+,(scr).w
            move.b    (a0)+,(ucr).w
            move.b    (a0)+,(rsr).w
            move.b    (a0)+,(tsr).w
            move.b    (sav_MFP+14).l,(tadr).w
            move.b    (sav_MFP+15).l,(tbdr).w
            move.b    (sav_MFP+16).l,(tcdr).w
            move.b    (sav_MFP+17).l,(tddr).w
            move.b    (gpip).w,d0
            move.b    (aer).w,d1
            eor.b     d1,d0
            btst      #7,d0
            bne       saveState_MFP2
            btst      #7,(ipra).w
            bne       saveState_MFP2
            bchg      #7,(aer).w
            bchg      #7,(aer).w
saveState_MFP2:btst   #6,d0
            bne       saveState_MFP3
            btst      #6,(ipra).w
            bne       saveState_MFP3
            bchg      #6,(aer).w
            bchg      #6,(aer).w
saveState_MFP3:btst   #5,d0
            bne       saveState_MFP4
            btst      #7,(iprb).w
            bne       saveState_MFP4
            bchg      #5,(aer).w
            bchg      #5,(aer).w
saveState_MFP4:btst   #4,d0
            bne       saveState_MFP5
            btst      #6,(iprb).w
            bne       saveState_MFP5
            bchg      #4,(aer).w
            bchg      #4,(aer).w
saveState_MFP5:btst   #3,d0
            bne       saveState_MFP6
            btst      #3,(iprb).w
            bne       saveState_MFP6
            bchg      #3,(aer).w
            bchg      #3,(aer).w
saveState_MFP6:btst   #2,d0
            bne       saveState_MFP7
            btst      #2,(iprb).w
            bne       saveState_MFP7
            bchg      #2,(aer).w
            bchg      #2,(aer).w
saveState_MFP7:btst   #1,d0
            bne       saveState_MFP8
            btst      #1,(iprb).w
            bne       saveState_MFP8
            bchg      #1,(aer).w
            bchg      #1,(aer).w
saveState_MFP8:btst   #0,d0
            bne       saveState_MFP9
            btst      #0,(iprb).w
            bne       saveState_MFP9
            bchg      #0,(aer).w
            bchg      #0,(aer).w
saveState_MFP9:rts

saveState_MFPsave:move.b (tcdcr).w,d0
            and.b     #7,d0
            move.b    d0,(tcdcr).w
            move.b    #4,(tcdr).w
            or.b      #$70,d0
            move.b    (a1),d2
            or.b      #7,d2
            move.b    d2,(a1)
            moveq     #1,d1
            lea       (tcdr).w,a1
            lea       (tcdcr).w,a2
            cmp.b     (a0),d1
            bne.s     $107f8
            move.b    d0,(a2)
saveState_MFPsavel:cmp.b (a0),d1
            bne       saveState_MFPsavee
            cmp.b     (a1),d1
            bne.s     saveState_MFPsavel
saveState_MFPsavee:move.b (a0),d0
            rts

***************************************************************************
*   save/restore the keyboard state
*   Reset IKBD on restore, and reset keyboard tables
***************************************************************************
saveState_Keyboard:tst.w (saveOrRestoreFlag).l
            bne       saveState_Keyboarde
            move.b    #3,(keyctl).w
            move.b    #$96,(keyctl).w
            move.l    #ikbdResetCommand,-(sp)
            move.w    #2,-(sp)
            move.w    #$19,-(sp)
            trap      #$e                       ; Ikbdws(2, ikbdResetCommand)
            addq.w    #8,sp
            move.l    #-1,-(sp)
            move.l    #-1,-(sp)
            move.l    #-1,-(sp)
            move.w    #$10,-(sp)
            trap      #$e                       ; Keytbl(-1L, -1L, -1L)
            adda.w    #$e,sp
            movea.l   d0,a0
            movea.l   (a0)+,a3
            movea.l   (a0)+,a4
            movea.l   (a0)+,a5
            move.w    #$18,-(sp)
            trap      #$e                       ; Bioskeys()
            addq.w    #2,sp
            move.l    a5,-(sp)
            move.l    a4,-(sp)
            move.l    a3,-(sp)
            move.w    #$10,-(sp)
            trap      #$e                       ; Keytbl(,,)
            adda.w    #$e,sp
saveState_Keyboarde:rts

ikbdResetCommand:DC.B $80,$01

***************************************************************************
*   save/restore the MIDI port state
*   Just reset MIDI on restore
***************************************************************************
saveState_MIDI:tst.w  (saveOrRestoreFlag).l
            bne       saveState_MIDIe
            move.b    #3,(midictl).w
            move.b    #$95,(midictl).w
saveState_MIDIe:rts

***************************************************************************
*   save/restore the sound chip state
***************************************************************************
saveState_PSG:movem.l d3-d4/a3,-(sp)
            moveq     #$f,d3
            lea       (save_psg).l,a3
            tst.w     (saveOrRestoreFlag).l
            beq       saveState_PSGe
            moveq     #$ff,d4
saveState_PSGl:addq.w #1,d4
            move.w    d4,-(sp)
            move.w    #0,-(sp)
            move.w    #$1c,-(sp)
            trap      #$e                       ; Giaccess()
            addq.w    #6,sp
            move.b    d0,(a3)+
            dbra      d3,saveState_PSGl
            ori.b     #$37,(save_psg+7).l
            movem.l   (sp)+,d3-d4/a3
            rts

saveState_PSGe:moveq  #$7f,d4
saveState_PSGel:addq.w #1,d4
            move.b    (a3)+,d0
            move.w    d4,-(sp)
            move.w    d0,-(sp)
            move.w    #$1c,-(sp)
            trap      #$e                       ; Giaccess()
            addq.w    #6,sp
            dbra      d3,saveState_PSGel
            movem.l   (sp)+,d3-d4/a3
            rts

***************************************************************************
*   save/restore the shifter state
***************************************************************************
saveState_video:tst.w (saveOrRestoreFlag).l
            beq       saveState_videoe
            move.b    (v_bas_h).w,(sav_v_bas_h).l
            move.b    (v_bas_m).w,(sav_v_bas_m).l
            move.b    (v_bas_l).w,(sav_v_bas_l).l
            move.b    (synmod+1).w,(sav_synmod).l
            move.w    (palette).w,(sav_palette0).l
            move.b    (v_shf_mod+1).w,(sav_v_shf_mod).l
            rts
saveState_videoe:move.b (sav_v_bas_h).l,(v_bas_h).w
            move.b    (sav_v_bas_m).l,(v_bas_m).w
            move.b    (sav_v_bas_l).l,(v_bas_l).w
            move.b    (sav_synmod).l,(synmod+1).w
            move.w    (sav_palette0).l,(palette).w
            move.b    (sav_v_shf_mod).l,(v_shf_mod+1).w
            move.b    #0,(v_pixscroll+1).w
            rts

***************************************************************************
*   save/restore the blitter halftone data
***************************************************************************
saveState_halftone:move.w #$1e,d0
            lea       (Halftone).w,a0
            lea       (sav_halftone).l,a1
            tst.w     (saveOrRestoreFlag).l
            beq       saveState_halftonee
saveState_halftonel1:move.w (a0)+,(a1)+
            dbra      d0,saveState_halftonel1
            rts
saveState_halftonee:move.w (a1)+,(a0)+
            dbra      d0,saveState_halftonee
            rts

***************************************************************************
*   save/restore the BIOS device driver state
*   Read the boot sector to detect disk change and update BIOS disk status
***************************************************************************
saveState_bios_rwabs:tst.w (saveOrRestoreFlag).l
            beq       saveState_bios_rwabsl
            move.w    #0,-(sp)
            move.w    #0,-(sp)
            move.w    #1,-(sp)
            move.l    #0,-(sp)
            move.w    #0,-(sp)
            move.w    #4,-(sp)
            trap      #$d                       ; Rwabs(0, 0L, 1, 0, 0)
            adda.w    #$e,sp
            move.w    #1,-(sp)
            move.w    #0,-(sp)
            move.w    #1,-(sp)
            move.l    #0,-(sp)
            move.w    #0,-(sp)
            move.w    #4,-(sp)
            trap      #$d                       ; Rwabs(0, 0L, 1, 0, 1)
            adda.w    #$e,sp
saveState_bios_rwabsl:rts

***************************************************************************
*   save/restore the FDC state
***************************************************************************
saveState_FDC:tst.w   (saveOrRestoreFlag).l
            beq.s     saveState_bios_rwabsl
            move.w    #$190,(diskctl).w
            move.w    #$90,(diskctl).w
            rts

***************************************************************************
*   save/restore the AHDI state
*   Send harddrive to sleep/wakeup
***************************************************************************
saveState_AHDI:tst.b  (ahdiDetected).l
            bne       saveState_AHDIe
saveState_AHDI6:rts

saveState_AHDIe:moveq #0,d0
            andi.w    #7,d0
            lsl.w     #4,d0
            move.b    d0,(ide_headn).l
            tst.w     (saveOrRestoreFlag).l
            beq       saveState_AHDIe2
            move.b    #$e0,(ide_comst).l        ; IDE standby immediate
saveState_AHDI7:move.l #10*200,d0
            add.l     (_hz_200).w,d0
saveState_AHDIl:btst  #5,(gpip).w
            beq.s     saveState_AHDI3
            btst      #7,(ide_stat2).l
            beq.s     saveState_AHDI3
            cmp.l     (_hz_200).w,d0
            bhi.s     saveState_AHDIl
            moveq     #-1,d0
            bra.s     saveState_AHDIr
saveState_AHDI3:move.b (ide_comst).l,d0         ; IDE status register
            btst      #0,d0
            bne.s     saveState_AHDI5
            btst      #3,d0
            bne.s     saveState_AHDIr
            moveq     #0,d0
            bra.s     saveState_AHDIr
saveState_AHDI5:move.b (ide_param).l,d0
saveState_AHDIr:rts

saveState_AHDIe2:bsr  saveState_AHDIs
            tst.w     d0
            beq.s     saveState_AHDI6
            moveq     #0,d0
            andi.w    #7,d0
            lsl.w     #4,d0
            move.b    d0,(ide_headn).l
            move.b    (ahdiStandbyTime).l,(ide_seccn).l
            move.b    #$e2,(ide_comst).l        ; IDE standby command
            bra.s     saveState_AHDI7
saveState_AHDIs:andi.b #7,d0
            lsl.b     #4,d0
            move.b    d0,(ide_headn).l
            move.l    #5*200,d0
            add.l     (_hz_200).w,d0
            move.b    #$50,d1
saveState_AHDIs2:cmp.b (ide_stat2).l,d1
            beq.s     saveState_AHDIs3
            cmp.l     (_hz_200).w,d0
            bcc.s     saveState_AHDIs2
            moveq     #0,d0
            rts
saveState_AHDIs3:moveq #1,d0
            rts

***************************************************************************
*   patch all vectors to monitor image updates and/or keyboard/mouse input
*   as well as timeouts for sleep
***************************************************************************
setupVectors:
            * patch AES/VDI trap
            moveq    #$ff,d0
            trap      #2
            move.l    d0,(sav_screendrvfuncptr).l
            move.l    (trap_aesvdi).w,(oldtrap_aesvdi).l
            move.l    #new_trap2,(trap_aesvdi).w

            * patch BIOS trap
            move.l    (trap_bios).w,(oldtrap_bios).l
            move.l    #new_bios,(trap_bios).w

            * patch XBIOS trap
            move.l    (trap_xbios).w,(oldtrap_xbios).l
            move.l    #new_xbios,(trap_xbios).w

            * patch line A
            linea     #0                        ; Line-A Initialization
            move.l    d0,(lineA_variables).l
            move.l    a1,(lineA_fontHeaders).l
            move.w    #15,d0
            lea       (sav_lineA).l,a3
setupVectorsl:move.l  (a2)+,(a3)+
            dbra      d0,setupVectorsl
            move.l    (lineAexception).w,(old_lineA).l
            move.l    #new_lineA,(lineAexception).w

            * patch IKBD vectors (keyboard, joystick, MIDI, mouse)
            move.w    #$22,-(sp)
            trap      #$e                       ; Kbdvbase()
            addq.w    #2,sp
            movea.l   d0,a1
            lea       $10(a1),a1
            move.l    a1,(old_mousevecAddr).l
            move.l    (a1),(old_mousevec).l
            move.l    #new_mousevec,(a1)
            movea.l   d0,a1
            lea       -4(a1),a1
            move.l    (a1),(old_ikbd_neg4).l
            move.l    #new_ikbd_neg4,(a1)
            movea.l   d0,a1
            lea       $18(a1),a1
            move.l    (a1),(old_joyvec).l
            move.l    #new_joyvec,(a1)
            movea.l   d0,a1
            lea       $1c(a1),a1
            move.l    (a1),(old_midisysvec).l
            move.l    #new_midisysvec,(a1)

            * patch 50Hz timer
            move.l    (etv_timer).w,(old_evt_timer).l
            move.l    #new_etv_timer,(etv_timer).w

            * insert code into VBL queue
            movea.l   (_vblqueue).w,a0
            addq.l    #4,a0
setupVectorsl2:tst.l  (a0)+
            bne.s     setupVectorsl2
            move.l    #new_vbl,-4(a0)
            rts

***************************************************************************
*   Called 50 times per second
*   - if serial port monitoring is active, re-enable LCD
*   - if screen saver is active and timeout is reached, disable LCD
*   - if sleep timeout is active and reached, sleep machine
***************************************************************************
new_etv_timer:btst    #2,(rsr).w                ; serial port active?
            beq       new_etv_timer2            ; (no)
            move.l    (cookie_sleepTimer).l,(sleepTimerCounter).l
            tst.w     (cookie_watchSerialPortFlag).l ;monitor the RS232?
            beq       new_etv_timer2            ; (no)
            move.l    (cookie_blankScreenTimer).l,(blankScreenTimerCounter).l
            bclr      #3,(lcdPowerControlShadow).w ;LCD on
            move.b    (lcdPowerControlShadow).w,(lcdPowerControl).l

new_etv_timer2:tst.l  (cookie_blankScreenTimer).l
            beq       new_etv_timer3
            subq.l    #1,(blankScreenTimerCounter).l
            bne       new_etv_timer3
            move.l    (cookie_blankScreenTimer).l,(blankScreenTimerCounter).l
            bset      #3,(lcdPowerControlShadow).w ;LCD off
            move.b    (lcdPowerControlShadow).w,(lcdPowerControl).l

new_etv_timer3:tst.l  (cookie_sleepTimer).l
            beq       new_etv_timer4
            subq.l    #1,(sleepTimerCounter).l
            bne       new_etv_timer4
            move.l    (cookie_sleepTimer).l,(sleepTimerCounter).l
            pea       new_etv_timer4(pc)
            move      sr,-(sp)
            movem.l   d0-d2,(saveRegs).l
            jmp       (gotoSleep).l

new_etv_timer4:move.l (old_evt_timer).l,-(sp)
            rts

***************************************************************************
*   VBL vector patch
*   - After 3 VBLs disable the combo -> shadow controller video transfer
***************************************************************************
new_vbl:    tst.w     (cookie_videoPowerSaverFlag).l    ; disable the LCD after timeout?
            beq       new_vbl2                  ; (no)
            subq.w    #1,(countdownVBLTimer).l
            bne       new_vbl2
            bset      #4,(lcdPowerControlShadow).w ;REFRESH_MACHINE output
            move.b    (lcdPowerControlShadow).w,(lcdPowerControl).l
            bset      #0,(synmod+1).l           ; disable combo -> shadow controller video transfer
new_vbl2:   nop
            rts

***************************************************************************
*   mouse vector patch
*   Any mouse activity:
*   - enables the transfer to the shadow controller for at least 3 VBL.
*   - reset screen saver, enable LCD
*   - re-trigger the sleep timer.
***************************************************************************
new_mousevec:move.w   #3,(countdownVBLTimer).l  ; reset VBL timer
            bclr      #0,(synmod+1).l           ; enable combo -> shadow controller video transfer
            move.l    (cookie_blankScreenTimer).l,(blankScreenTimerCounter).l
            bclr      #3,(lcdPowerControlShadow).w ;LCD on
            move.b    (lcdPowerControlShadow).w,(lcdPowerControl).l
            move.l    (cookie_sleepTimer).l,(sleepTimerCounter).l

            move.l    (old_mousevec).l,-(sp)
            rts

***************************************************************************
*   joystick vector patch
*   Any joystick activity:
*   - enables the transfer to the shadow controller for at least 3 VBL.
*   - reset screen saver, enable LCD
*   - re-trigger the sleep timer.
***************************************************************************
new_joyvec: move.w    #3,(countdownVBLTimer).l  ; reset VBL timer
            bclr      #0,(synmod+1).l           ; enable combo -> shadow controller video transfer
            move.l    (cookie_blankScreenTimer).l,(blankScreenTimerCounter).l
            bclr      #3,(lcdPowerControlShadow).w ;LCD on
            move.b    (lcdPowerControlShadow).w,(lcdPowerControl).l
            move.l    (cookie_sleepTimer).l,(sleepTimerCounter).l

            move.l    (old_joyvec).l,-(sp)
            rts

***************************************************************************
*   keyboard driver patch
*   Any keyboard activity:
*   - enables the transfer to the shadow controller for at least 3 VBL.
*   - reset screen saver, enable LCD
*   - re-trigger the sleep timer.
***************************************************************************
new_ikbd_neg4:move.w  #3,(countdownVBLTimer).l  ; reset VBL timer
            bclr      #0,(synmod+1).l           ; enable combo -> shadow controller video transfer
            move.l    (cookie_blankScreenTimer).l,(blankScreenTimerCounter).l
            bclr      #3,(lcdPowerControlShadow).w ;LCD on
            move.b    (lcdPowerControlShadow).w,(lcdPowerControl).l
            move.l    (cookie_sleepTimer).l,(sleepTimerCounter).l

            move.l    (old_ikbd_neg4).l,-(sp)
            rts

***************************************************************************
*   MIDI vector patch
*   - any MIDI activity re-triggers the sleep timer.
***************************************************************************
new_midisysvec:move.l (cookie_sleepTimer).l,(sleepTimerCounter).l
            move.l    (old_midisysvec).l,-(sp)
            rts

***************************************************************************
*   AES/VDI patch
*   - #-1 enables the transfer to the shadow controller for at least 3 VBL.
*   - VDI calls except calls waiting for keyboard input also enable the
*     transfer to the shadow controller for at least 3 VBL.
*   - VDI v_opnwk() patches the mouse vector on the first call.
***************************************************************************
new_trap2:  cmp.w     #$73,d0                   ; VDI?
            bne       new_trap2b                ; (no)
            move.l    a0,-(sp)
            movea.l   d1,a0
            movea.l   (a0),a0
            move.w    (a0),(vdi_functionCode).l
            movea.l   (sp)+,a0
            cmpi.w    #$21,(vdi_functionCode).l ; vsin_mode() - Set input mode
            beq       new_trap2out
            cmpi.w    #$1f,(vdi_functionCode).l ; vrq_string() / vsm_string() - keyboard string input
            beq       new_trap2out
            cmpi.w    #$80,(vdi_functionCode).l ; vq_key_s() - Get shift key status
            beq       new_trap2out
            move.l    #trap_vdi_tailPatch,-(sp)
            move.w    4(sp),-(sp)
            ori.w     #$2000,(sp)
            move.l    (oldtrap_aesvdi).l,-(sp)
            rts

new_trap2b: cmp.w     #$ffff,d0
            bne       new_trap2out
            move.l    #trap_aesvdi_m1,d0
            rte

new_trap2out:move.l   (oldtrap_aesvdi).l,-(sp)
            rts

trap_vdi_tailPatch:move.w #3,(countdownVBLTimer).l ;reset VBL timer
            bclr      #0,(synmod+1).l           ; enable combo -> shadow controller video transfer
            cmpi.w    #1,(vdi_functionCode).l   ; v_opnwk()
            beq       trap_aesvdi_tailPatch2
            rte

trap_aesvdi_tailPatch2:bset #0,(v_opnwk_called).l
            bne       trap_aesvdi_tailPatch3
            move.l    a1,-(sp)
            movea.l   (old_mousevecAddr).l,a1
            move.l    (a1),(old_mousevec).l
            move.l    #new_mousevec,(a1)
            movea.l   (sp)+,a1
trap_aesvdi_tailPatch3:rte

trap_aesvdi_m1:movea.l (sav_screendrvfuncptr).l,a0
            jsr       (a0)
            move.w    #3,(countdownVBLTimer).l  ; reset VBL timer
            bclr      #0,(synmod+1).l           ; enable combo -> shadow controller video transfer
            rts

***************************************************************************
*   BIOS patch
*   - Bconout() enables the transfer to the shadow controller
*     for at least 3 VBL.
*   - Bconin(), Rwabs() and Getbpb() re-trigger the sleep timer.
***************************************************************************
new_bios:   move      usp,a0                    ; USP stack is active
            btst      #5,(sp)                   ; called from user mode?
            beq       new_bios2                 ; yes ->
            movea.l   sp,a0                     ; SSP stack is active
            addq.l    #6,a0
new_bios2:  move.w    (a0),d0                   ; bios function number
            cmp.w     #3,d0                     ; Bconout()
            beq       new_bios_bconout
            cmp.w     #2,d0                     ; Bconin()
            beq       new_bios_updatetimer
            cmp.w     #4,d0                     ; Rwabs()
            beq       new_bios_updatetimer
            cmp.w     #7,d0                     ; Getbpb()
            beq       new_bios_updatetimer
new_bios_old:movea.l  (oldtrap_bios).l,a0
            jmp       (a0)

new_bios_updatetimer:move.l (cookie_sleepTimer).l,(sleepTimerCounter).l
            bra.s     new_bios_old

new_bios_bconout:move.l (cookie_sleepTimer).l,(sleepTimerCounter).l
            move.l    2(a0),-(sp)
            move.w    #3,-(sp)                  ; Bconout()
            pea       new_bios_tailPatch(pc)
            move.w    $a(sp),-(sp)
            ori.w     #$2000,(sp)
            movea.l   (oldtrap_bios).l,a0
            jmp       (a0)

new_bios_tailPatch:move.w #3,(countdownVBLTimer).l ;reset VBL timer
            bclr      #0,(synmod+1).l           ; enable combo -> shadow controller video transfer
            addq.l    #6,sp
            rte

***************************************************************************
*   XBIOS patch
*   - Setscreen() and Vsync() enable the transfer to the shadow controller
*     for at least 3 VBL.
*   - Midiws(), Flopver() and DMAread()/DMAwrite() re-trigger the
*     sleep timer.
***************************************************************************
new_xbios:  move      usp,a0                    ; USP stack is active
            btst      #5,(sp)                   ; called from user mode?
            beq       new_xbios2                ; yes ->
            lea       6(sp),a0                  ; SSP stack is active
new_xbios2: move.w    (a0),d0                   ; xbios function number
            cmp.w     #5,d0                     ; Setscreen()
            beq       new_xbios_setscreen
            cmp.w     #$25,d0                   ; Vsync()
            beq       new_xbios4
            cmp.w     #8,d0                     ; Floprd()
            blt       new_xbios_old
            cmp.w     #$c,d0                    ; Midiws()
            ble       new_xbios_updatetimer
            cmp.w     #$13,d0                   ; Flopver()
            beq       new_xbios_updatetimer
            cmp.w     #$2a,d0                   ; DMAread()
            beq       new_xbios_updatetimer
            cmp.w     #$2b,d0                   ; DMAwrite()
            beq       new_xbios_updatetimer
new_xbios_old:movea.l (oldtrap_xbios).l,a0
            jmp       (a0)

new_xbios_updatetimer:move.l (cookie_sleepTimer).l,(sleepTimerCounter).l
            bra.s     new_xbios_old

new_xbios_setscreen:move.w $a(a0),-(sp)         ; mode
            move.l    6(a0),-(sp)               ; physbase
            move.l    2(a0),-(sp)               ; logbase
            move.w    #5,-(sp)                  ; # Setscreen()
            pea       new_xbios_tailPatch(pc)
            move.w    $10(sp),-(sp)
            ori.w     #$2000,(sp)
            movea.l   (oldtrap_xbios).l,a0
            jmp       (a0)

new_xbios_tailPatch:move.w #3,(countdownVBLTimer).l ;reset VBL timer
            bclr      #0,(synmod+1).l           ; enable combo -> shadow controller video transfer
            adda.l    #$c,sp                    ; remove parameters and function number from the stack
            rte

new_xbios4: move.w    #3,-(sp)                  ; # Logbase()
            pea       new_xbios7(pc)            ; push return address onto stack
            move.w    6(sp),-(sp)               ; push SR onto stack (for a rte)
            ori.w     #$2000,(sp)               ; set supervisor mode bit
            movea.l   (oldtrap_xbios).l,a0
            jmp       (a0)

new_xbios7: move.w    #3,(countdownVBLTimer).l  ; reset VBL timer
            bclr      #0,(synmod+1).l           ; enable combo -> shadow controller video transfer
            addq.l    #2,sp                     ; remove function number from the stack
            rte

***************************************************************************
*   line A patch
*   After every line A operation the video transfer to the shadow controller
*   is enabled and kept on for at least 3 VBLs. Without this the video
*   changes would not be visible to the user.
***************************************************************************
new_lineA:  movea.l   2(sp),a1                  ; PC which points to the current opcode
            move.w    (a1),d2                   ; actual line-A opcode
            and.w     #$fff,d2                  ; mask our the upper 4 bit ($Axxx)
            addq.l    #2,a1                     ; increment PC by 2 (behind the lineA opcode)
            move.l    a1,2(sp)                  ; write back new PC
            cmp.w     #$f,d2                    ; lineA range 0..15
            bhi       new_lineA2                ; out of range...punt
            lsl.w     #2,d2                     ; pointer into lineA jump table
            movea.l   new_lineA_FuncPtr(pc,d2.w),a1
            movem.l   d3-d7/a3-a5,-(sp)         ; save all registers
            jsr       (a1)                      ; call our lineA handler
            movem.l   (sp)+,d3-d7/a3-a5         ; restore all registers
new_lineA2: rte                                 ; return from interrupt

new_lineA_FuncPtr:
            DC.L lineA_0,lineA_1,lineA_2,lineA_3,lineA_4,lineA_5,lineA_6,lineA_7
            DC.L lineA_8,lineA_9,lineA_a,lineA_b,lineA_c,lineA_d,lineA_e,lineA_f

lineA_0:    movea.l   (lineA_variables).l,a0
            move.l    a0,d0
            movea.l   (lineA_fontHeaders).l,a1
            lea       (new_lineA_FuncPtr).l,a2
            rts

lineA_1:    pea       lineA_tailPatch(pc)
            move.l    (sav_lineA+4).l,-(sp)
            rts

lineA_2:    pea       lineA_tailPatch(pc)
            move.l    (sav_lineA+4*2).l,-(sp)
            rts

lineA_3:    pea       lineA_tailPatch(pc)
            move.l    (sav_lineA+4*3).l,-(sp)
            rts

lineA_4:    pea       lineA_tailPatch(pc)
            move.l    (sav_lineA+4*4).l,-(sp)
            rts

lineA_5:    pea       lineA_tailPatch(pc)
            move.l    (sav_lineA+4*5).l,-(sp)
            rts

lineA_6:    pea       lineA_tailPatch(pc)
            move.l    (sav_lineA+4*6).l,-(sp)
            rts

lineA_7:    pea       lineA_tailPatch(pc)
            move.l    (sav_lineA+4*7).l,-(sp)
            rts

lineA_8:    pea       lineA_tailPatch(pc)
            move.l    (sav_lineA+4*8).l,-(sp)
            rts

lineA_9:    pea       lineA_tailPatch(pc)
            move.l    (sav_lineA+4*9).l,-(sp)
            rts

lineA_a:    pea       lineA_tailPatch(pc)
            move.l    (sav_lineA+4*10).l,-(sp)
            rts

lineA_b:    pea       lineA_tailPatch(pc)
            move.l    (sav_lineA+4*11).l,-(sp)
            rts

lineA_c:    pea       lineA_tailPatch(pc)
            move.l    (sav_lineA+4*12).l,-(sp)
            rts

lineA_d:    pea       lineA_tailPatch(pc)
            move.l    (sav_lineA+4*13).l,-(sp)
            rts

lineA_e:    pea       lineA_tailPatch(pc)
            move.l    (sav_lineA+4*14).l,-(sp)
            rts

lineA_f:    pea       lineA_tailPatch(pc)
            move.l    (sav_lineA+4*15).l,-(sp)
            rts

lineA_tailPatch:move.w #3,(countdownVBLTimer).l ; reset VBL timer
            bclr      #0,(synmod+1).l           ; enable combo -> shadow controller video transfer
            rts

***************************************************************************
*
***************************************************************************
infoText:           DC.B '**********************************\r\n'
                    DC.B 'Shutdown   version 1.0   Oct 17 91\r\n'
                    DC.B 'Copyright  1991 Atari Corporation\r\n'
                    DC.B 'All Rights Reserved\r\n'
                    DC.B '**********************************\r\n'
                    DC.B 0
oldcookieresvector: DS.L 1
oldcookieresvalid:  DS.L 1
saveOrRestoreFlag:  DS.B 1
oldresvalid:        DS.L 1
oldresvector:       DS.L 1
sav_v_bas_h:        DS.B 1
sav_v_bas_m:        DS.B 1
sav_v_bas_l:        DS.B 1
sav_synmod:         DS.B 1
sav_palette0:       DS.W 1
sav_v_shf_mod:      DS.B 1
save_psg:           DS.B 16
saveRegs:           DS.L 16
oldSSP:             DS.L 1
oldUSP:             DS.L 1
sav_MFP:            DS.B 18
sav_halftone:       DS.W 31
sav_screendrvfuncptr:DS.L 1
cookiePtr:          DS.L 1
old_mousevec:       DS.L 1
old_mousevecAddr:   DS.L 1
old_joyvec:         DS.L 1
old_ikbd_neg4:      DS.L 1
old_midisysvec:     DS.L 1
lineA_variables:    DS.L 1
lineA_fontHeaders:  DS.L 1
countdownVBLTimer:  DS.W 1
vdi_functionCode:   DS.W 1
ahdiDetected:       DS.B 1
ahdiStandbyTime:    DS.B 1
blankScreenTimerCounter:DS.L 1
sleepTimerCounter:  DS.L 1
cookie_blankScreenTimer:DS.L 1
cookie_sleepTimer:  DS.L 1
cookie_videoPowerSaverFlag:DS.W 1
cookie_watchSerialPortFlag:DS.W 1
old_evt_timer:      DS.L 1
oldtrap_aesvdi:     DS.L 1
oldtrap_bios:       DS.L 1
oldtrap_xbios:      DS.L 1
old_lineA:          DS.L 1
sav_lineA:          DS.L 16
v_opnwk_called:     DS.B 1