diff --git a/BootPicker.a b/BootPicker.a new file mode 100644 index 0000000..54483cd --- /dev/null +++ b/BootPicker.a @@ -0,0 +1,309 @@ +; Inputs: append 1 or more Pascal strings, followed by zero byte +; Outputs: D0 = index of selected string, Ao = pointer to selected string + + +; Aesthetic parameters +kMinWid equ 70 ; min width to leave for button label +kBtnHt equ 20 ; should always be 20 because text won't move +kBtnGap equ 13 +kPadX equ 5+26 ; due to border, padding=6 would leave 1 white pixel +kPadY equ 5+13 + + +; Save all the stuff that we whack + movem.l A1-A5/D1-D7,-(SP) + move.l $3F8,-(SP) ; Save DSAlertRect + move.l $3F8+4,-(SP) + move.l $9CE,-(SP) ; Save ToolScratch + move.l $9CE+4,-(SP) + move.l $2BA,-(SP) ; Save DSAlertTab + link.w A6,#0 ; We use a lot of stack + + +; Head-patch _PtInRect to account for our unusual window placement + move.w #$A8AD,D0 ; _PtInRect + dc.w $A146 ; _GetTrapAddress + move.l A0,$9CE+4 ; Save in ToolScratch+4 to enable head-patch + + move.w #$A8AD,D0 ; _PtInRect + lea MyPtInRect,A0 + dc.w $A047 ; _SetTrapAddress + + +; Do the usual "QuickDraw outside of application" setup + clr.l -(SP) + move.l SP,A5 ; tiny A5 world with QD globals ptr + lea -206(SP),SP ; push the QD globals + pea 202(SP) + dc.w $A86E ; _InitGraf + dc.w $A8FE ; _InitFonts (because we use _StringWidth) + lea -108(SP),SP + pea (SP) + dc.w $A86F ; _OpenPort (don't save, we have our own globs) + + +; Copy a stub alert table to the stack + move.l #MyDSAlertTabEnd-MyDSAlertTabStart,D0 + sub.l D0,SP + move.l SP,A1 + lea MyDSAlertTabStart,A0 + dc.w $A22E ; _BlockMoveData + + +; First pass: calculate box width, putting shortest string width into D5 + moveq.l #kMinWid,D5 + lea TrailingList,A0 ; A0 = current pstring ptr +dswLoop + tst.b (A0) + beq.s dswDone + + move.l A0,-(SP) + clr.w -(SP) + move.l A0,-(SP) + dc.w $A88C ; _StringWidth + move.w (SP)+,D0 + addq.w #8,D0 + cmp.w D0,D5 + bgt.s .notlargest + move.w D0,D5 +.notlargest + move.l (SP)+,A0 + + moveq.l #1,D0 ; increment the pstring ptr + add.b (A0),D0 + add.l D0,A0 + bra.s dswLoop +dswDone + + +; Second pass: create a button list object in the DS alert table + move.w (SP)+,D7 ; pop NumEntries from DS table + + move.l #((kPadY)<<16)|(kPadX),D2 ; D2 = current button's topleft + move.l #((kPadY+kBtnHt)<<16)|(kPadX+4+4),D3; D3 = current button's botright + add.w D5,D3 ; ...including width of the text + moveq.l #0,D6 ; D6 = button counter + lea TrailingList,A0 ; A0 = current pstring ptr +dsbLoop + tst.b (A0) + beq.s dsbDone + + move.w D6,-(SP) ; push procedure object ID (base $8000) + add.w #$8000,(SP) + move.l D3,-(SP) ; push botright + move.l D2,-(SP) ; push topleft + move.w D6,-(SP) ; push string object ID (base $C000) + add.w #$C000,(SP) + ; increment... + add.l #(kBtnHt+kBtnGap)<<16,D2 ; the button's top edge + add.l #(kBtnHt+kBtnGap)<<16,D3 ; the button's bottom edge + addq.w #1,D6 ; the button counter + moveq.l #1,D0 ; the pstring ptr + add.b (A0),D0 + add.l D0,A0 + bra.s dsbLoop + +dsbDone ; done looping, push a header for this object... + move.w D6,-(SP) ; the number of buttons + mulu #12,D6 ; the size in bytes = 12n+2 + add.w #2,D6 + move.w D6,-(SP) + move.w #$1993,-(SP) ; the unique ID for this button list + + addq.w #1,D7 + move.w D7,-(SP) ; increment and re-push NumEntries + + +; Third pass: calculate box height + lea $3F8,A1 ; A0 = DSAlertRect + move.l (A5),A0 ; From QD globals + move.l -122+6+4(A0),D0 ; D0 = screenBits.bounds.botRight, so D0.W = screen width + + add.w #kPadX,D3 ; D3 = box width + sub.w D3,D0 + bgt.s .nottoowide + clr.w D0 +.nottoowide + asr.w #1,D0 + move.w D0,2(A1) ; set DSAlertRect left + move.w D0,$9CE+2 ; & stash in ToolScratch for MyPtInRect + add.w D3,D0 + move.w D0,6(A1) ; set DSAlertRect right + + swap D0 ; D0 = screen height + swap D2 ; D2 = box height ("top" of next button) + add.w #kPadY-kBtnGap,D2 ; adjust box height + sub.w D2,D0 + bgt.s .nottoohigh + clr.w D0 +.nottoohigh + asr.w #1,D0 + move.w D0,(A1) ; set DSAlertRect top + move.w D0,$9CE ; & stash in ToolScratch for MyPtInRect + add.w D2,D0 + move.w D0,4(A1) ; set DSAlertRect bottom + + +; Fourth pass: create a string offset for each button + moveq.l #0,D6 ; D6 = button counter + lea TrailingList,A0 ; A0 = current pstring ptr +dssLoop + tst.b (A0) + beq.s dssDone + + move.w (SP)+,D7 ; pop NumEntries from DS table + + ; push the struct fields... + bsr PushPaddedString ; even-length string with 2-byte length + move.w D6,-(SP) ; the unique ID (C000+n) + add.w #$C000,(SP) + + addq.w #1,D7 + move.w D7,-(SP) ; increment and re-push NumEntries + + ; increment... + addq.w #1,D6 ; the button counter + moveq.l #1,D0 ; the pstring ptr + add.b (A0),D0 + add.l D0,A0 + bra.s dssLoop +dssDone + + +; Fifth pass: generate code for each button + moveq.l #0,D6 ; D6 = button counter + lea TrailingList,A0 ; A0 = current pstring ptr +dspLoop + tst.b (A0) + beq.s dspDone + + move.w (SP)+,D7 ; pop NumEntries from DS table + + pea ReturnToCaller ; craft the code for this button... + move.w #$4EF9,-(SP) ; jmp ReturnToCaller (absolute addr) + move.l A6,-(SP) + move.w #$2C7C,-(SP) ; move.l #CorrectA6,A6 ; frame ptr + move.l A0,-(SP) + move.w #$207C,-(SP) ; move.l #CorrectA0,A0 ; return string ptr + move.l D6,-(SP) + move.w #$203C,-(SP) ; move.l #CorrectD0,D0 ; return string idx + + ; push a header... + move.w #24,-(SP) ; the size in bytes of the above code + move.w D6,-(SP) ; the unique ID (8000+n) + add.w #$8000,(SP) + + addq.w #1,D7 + move.w D7,-(SP) ; increment and re-push NumEntries + + ; increment... + addq.w #1,D6 ; the button counter + moveq.l #1,D0 ; the pstring ptr + add.b (A0),D0 + add.l D0,A0 + bra.s dspLoop + +dspDone ; Must clear the instruction cache on 030/040 + move.l A6,D0 + sub.l SP,D0 + move.l SP,A0 + move.l SP,A1 + dc.w $A02E ; using non-Data _BlockMove + + +; Put up the DeepShit alert + move.l SP,$2BA ; Set DSAlertTab + clr.l $A8C ; Clear RestProc or our buttons get jumbled + move.w #$2012,D0 ; Our error object number + dc.w $A9C9 ; _SysError! + bra.s * ; Should never return + + +; The selected procedure will jump to here after setting A6,A0,D0 +ReturnToCaller + move.w #$A8AD,D0 ; Unpatch _PtInRect + move.l $9CE+4,A0 ; We saved it in ToolScratch + dc.w $A047 ; _SetTrapAddress + + unlk A6 ; See the saving code for details + move.l (SP)+,$2BA + move.l (SP)+,$9CE+4 + move.l (SP)+,$9CE + move.l (SP)+,$3F8+4 + move.l (SP)+,$3F8 + movem.l (SP)+,A1-A5/D1-D7 + rts + + +PushPaddedString ; trashes A1-A2,D0-D2, but must preserve A0 + move.l (SP)+,A2 + + move.l A0,-(SP) ; backup + moveq.l #0,D0 + clr.w -(SP) + move.l A0,-(SP) + dc.w $A88C ; _StringWidth + move.w D5,D0 + sub.w (SP)+,D0 + bgt.s .notzero + moveq.l #0,D0 +.notzero + asr.w #3,D0 ; divide the leftover by twice a Chicago space + move.l (SP)+,A0 ; restore backup + + clr.w D1 + move.b D0,D1 + add.b (A0),D1 + addq.b #1,D1 + bclr #0,D1 ; this is the spaced-out length in memory + + clr.w D2 +.spaceloop + cmp.w D2,D1 + beq.s .spacedone + move.w #$2020,-(SP) + addq.w #2,D2 + bra.s .spaceloop +.spacedone + move.w D1,-(SP) + + + lea 2(SP),A1 ; bm dest + add.w D0,A1 + + clr.l D0 + move.b (A0),D0 + move.l A0,-(SP) + addq.l #1,A0 ; bm src + dc.w $A22E ; _BlockMoveData + move.l (SP)+,A0 + + + jmp (A2) + + +MyPtInRect + move.l $9CE,D0 + sub.w D0,10(SP) + swap D0 + sub.w D0,8(SP) + + move.l $9CE+4,A0 + jmp (A0) + + +MyDSAlertTabStart + dc.w 1 ; NumEntries + +; Our only alert definition + dc.w $2012 ; ID of this error + dc.w 10 ; length of remaining items + dc.w 0 ; primary text definition ID + dc.w 0 ; secondary text definition ID + dc.w 0 ; icon definition ID + dc.w 0 ; procedure definition ID + dc.w $1993 ; button definition ID +MyDSAlertTabEnd + + +TrailingList diff --git a/Makefile b/Makefile index dfcf7a2..61ce390 100644 --- a/Makefile +++ b/Makefile @@ -63,5 +63,17 @@ pyserver: payload FORCE +testpicker: BootPicker.bin FORCE + cp sys701-144.img bootpick.tmp + dd if=BootPicker.bin of=bootpick.tmp bs=138 seek=1 conv=notrunc + Mini\ vMac\ Classic.app/Co*/Ma*/* xo.rom bootpick.tmp + +BootPicker.bin: BootPicker.a + vasm-1/vasmm68k_mot -quiet -Fbin -pic -o $@ $< + + + + + FORCE: