;=============================================================================
; windemo.asm - one X-COM style window: a text line, a number, and an up/down
;   spinner. Self-contained: no disk assets, no game code. Assemble with mads:
;
;       mads windemo.asm -o:windemo.xex
;
; What it shows, in the order the code does it:
;
;   1. There is no ANTIC picture at all. Screen DMA is switched off and the whole
;      320x200 image is a VBXE overlay framebuffer at VRAM $000000, one byte per
;      pixel, 256 colours out of VBXE palette #1. That is exactly how the UFO port
;      runs -- no character mode, no display list, no playfield.
;
;   2. Text is not a hardware character mode either. A font "sheet" lives in VRAM
;      and each glyph is blitted into the framebuffer by the VBXE blitter, one
;      blit per character. To keep this file free of data blobs, the sheet is
;      built at runtime from the Atari OS ROM charset at $E000 (see font_expand):
;      every glyph pixel becomes 255, every gap 0. Blitting with AND = colour then
;      stamps the glyph in any palette index you like -- one font, any colour.
;      (The real game bakes its font from the original UFO's SMALLSET.DAT the same
;      way, plus a second copy with the original two-tone colours baked in.)
;
;   3. The window is a filled rectangle for the border and a second, inset one for
;      the face. The game blits a crop of the original .SCR artwork in there
;      instead, but the geometry is this.
;
;   4. The up/down icons are the real thing: a 1:1 transcription of OpenXcom's
;      ArrowButton::draw() (src/Interface/ArrowButton.cpp:94-303). An X-COM arrow
;      button is NOT a bare triangle -- it is a bevelled box (top/left _color+2,
;      bottom/right _color+5, face _color+4, three corner pixels poked by hand)
;      with a 9px triangle and a 3x3 stem inside it, all in _color+1. Holding it
;      down runs ImageButton::mousePress -> invert(_color+3), which maps a pixel
;      p to 2*(_color+3)-p; for _color+N that is simply _color+(6-N). So the same
;      five colours, read backwards, give you the pressed look for free.
;
;   Controls: joystick 0 up/down changes the number (click, then auto-repeat with
;   acceleration, like the original). There is no exit -- switch the machine off.
;
; Needs a VBXE. Without one it just flashes the border and stops.
;=============================================================================

; ---- Atari OS / hardware ----
SDMCTL   = $022F                ; OS shadow of DMACTL; the VBI copies it every frame
COLOR4   = $02C8
CHARSET  = $E000                ; OS ROM charset: 128 glyphs x 8 bytes, internal order
PORTA    = $D300                ; joystick 0 in bits 0..3 (up, down, left, right; 0 = pushed)
DMACTL   = $D400
VCOUNT   = $D40B

; ---- VBXE ----
VBXE_VCTL       = $D640
VBXE_XDL0       = $D641
VBXE_XDL1       = $D642
VBXE_XDL2       = $D643
VBXE_CSEL       = $D644
VBXE_PSEL       = $D645
VBXE_CR         = $D646
VBXE_CG         = $D647
VBXE_CB         = $D648
VBXE_BL_ADR0    = $D650
VBXE_BL_ADR1    = $D651
VBXE_BL_ADR2    = $D652
VBXE_BLITTER    = $D653
VBXE_MEMAC_CTRL = $D65E
VBXE_BANK_SEL   = $D65F

VC_XDL_ON = $01
VC_XCOLOR = $02
MC_CPU    = $08
BANK_EN   = $80

MEMW      = $9000               ; the 4K CPU window onto VRAM (MEMAC-A)
SCR_W     = 320
SCR_H     = 200

BANK_XDL  = $7F                 ; XDL at $07F000, blitter control block at $07F100
BCB_OFF   = $100
FONT_BANK = $38                 ; font sheet at $038000: 64 glyphs x 8x8, padded to 64 B
FONT_HI   = $80                 ; so glyph addr = $038000 + (gi<<6)
FONT_B2   = $03

; ---- palette indices. Deliberately laid out like an X-COM 16-colour ramp: the
;      LOWER the index the lighter the colour, so the widget's _color+1..+5 come
;      out light-to-dark and its base colour can simply be 0. ----
C_ARROW   = 1                   ; _color+1  arrow + top-left corner pixel
C_BEV_LT  = 2                   ; _color+2  top/left bevel
C_OUTLINE = 3                   ; _color+3  (the small arrow's outline; unused here)
C_FACE    = 4                   ; _color+4  button face
C_BEV_DK  = 5                   ; _color+5  bottom/right bevel
C_BORDER  = 6                   ; window border
C_WIN     = 7                   ; window face
C_TEXT    = 8
C_TITLE   = 9
C_VALUE   = 10
C_HINT    = 11

; ---- layout ----
WIN_X   = 48
WIN_Y   = 52
WIN_W   = 224
WIN_H   = 96
TXT_X   = WIN_X+12
TITLE_Y = WIN_Y+12
LABEL_Y = WIN_Y+44
VAL_X   = TXT_X+120             ; clear of the label: 'SCIENTISTS>' is 11 glyphs x 8 px
HINT_X  = 68                    ; (320 - 23 glyphs x 8) / 2
HINT_Y  = 168

AW_W    = 13                    ; ArrowButton(ARROW_BIG_UP, 13, 14, ...)
AW_H    = 14
AW_X    = WIN_X+192
AW_UP_Y = WIN_Y+36
AW_DN_Y = AW_UP_Y+20            ; ResearchInfoState.cpp:80-81 spaces its pair 20px apart

REP_DELAY = 16                  ; frames held before the first auto-repeat
REP_FIRST = 6                   ; initial repeat interval, in frames
REP_MIN   = 2                   ; fastest interval after acceleration

; ---- zero page (spare OS bytes) ----
srcp    = $CB                   ; 2 - font_expand source
dstp    = $CD                   ; 2 - font_expand destination
opp     = $CF                   ; 2 - aw_run's op-list pointer
; $D4..$D9 is the OS floating-point scratch (FR0). We never call the FP pack, so
; it is ours; $D2/$D3 are less reliably free, hence the jump.
calc_out = $D4                  ; 3 - 24-bit VRAM address
txt_ptr = $D7                   ; 2 - draw_text's string

        org $2000

;=============================================================================
; main
;=============================================================================
.proc main
        jsr detect_vbxe
        bcs ?ok
        lda #$34                ; no VBXE -> red border, park
        sta COLOR4
        jmp *
?ok     lda #0
        sta SDMCTL              ; ANTIC playfield off. Setting the SHADOW is what
        sta DMACTL              ;   makes it stick: the OS VBI reloads DMACTL from it.
        lda #$90+MC_CPU         ; MEMAC-A: map a 4K VRAM window at $9000, CPU side
        sta VBXE_MEMAC_CTRL
        lda #0
        sta VBXE_VCTL
        jsr setup_xdl
        jsr blit_init
        jsr load_pal
        jsr font_expand
        jsr enable_display
        jsr draw_screen
        jmp loop
.endp

;=============================================================================
; loop - one pass per frame: read the stick, run the auto-repeat, redraw only
;   what changed (the pressed widget, and the number).
;=============================================================================
held     dta $FF                ; 0 = up widget held, 1 = down, $FF = neither
shown    dta $FF                ; what the screen currently shows
value    dta 12                 ; the number the spinner edits (0..99)
rep_act  dta 0
rep_tim  dta 0
rep_rate dta 0

.proc loop
        jsr wait_frame
        lda #$FF                ; --- which widget is the stick on? ---
        sta held
        lda PORTA
        lsr                     ; C = bit 0 = joystick up (0 while pushed)
        bcc ?up
        lsr                     ; C = bit 1 = joystick down
        bcs ?rep
        lda #1
        sta held
        bne ?rep                ; (A = 1, always)
?up     lda #0
        sta held
?rep    lda held                ; --- press-state redraw, only when it changes ---
        cmp shown
        beq ?tick
        sta shown
        jsr draw_arrows
?tick   lda held                ; --- auto-repeat: fire on the press, then speed up ---
        cmp #$FF
        beq ?off
        lda rep_act
        bne ?run
        lda #1
        sta rep_act
        lda #REP_DELAY
        sta rep_tim
        lda #REP_FIRST
        sta rep_rate
        jmp ?fire               ; the press itself fires straight away
?run    dec rep_tim
        beq ?due
        jmp loop                ; not this frame
?due    lda rep_rate
        sta rep_tim
        lda rep_rate            ; accelerate: rate = max(REP_MIN, rate-1)
        cmp #REP_MIN
        beq ?fire
        dec rep_rate
?fire   lda held
        bne ?less
        lda value               ; up widget -> increase, clamped at 99
        cmp #99
        bcc ?more
        jmp loop
?more   inc value
        jmp ?drew
?less   lda value               ; down widget -> decrease, clamped at 0
        bne ?dec
        jmp loop
?dec    dec value
?drew   jsr draw_num
        jmp loop
?off    lda #0
        sta rep_act
        jmp loop
.endp

; wait_frame: spin until the beam wraps. No VBI hook needed for a demo this small.
.proc wait_frame
?w1     lda VCOUNT
        cmp #124
        bcc ?w1
?w2     lda VCOUNT
        cmp #124
        bcs ?w2
        rts
.endp

;=============================================================================
; VBXE bring-up
;=============================================================================
.proc detect_vbxe               ; C=1 if a VBXE answers at either core address
        lda VBXE_VCTL
        cmp #$10
        beq ?yes
        lda $D740
        cmp #$10
        beq ?yes
        clc
        rts
?yes    sec
        rts
.endp

; The XDL (VBXE's own display list): 8 overscan lines, then 200 active lines of a
; linear 320-byte-stride overlay reading VRAM $000000 through palette #1.
.proc setup_xdl
        lda #BANK_EN+BANK_XDL
        sta VBXE_BANK_SEL
        ldx #xdl_len-1
?l      lda xdl_data,x
        sta MEMW,x
        dex
        bpl ?l
        rts
.endp
xdl_data
        dta $74,$08             ; overscan block, OVOFF
        dta 7                   ; 8 lines
        dta $00,$00,$00         ; overlay address
        dta $40,$01             ; stride 320
        dta $11,$FF             ; overlay attributes
        dta $62,$88             ; GMON|RPTL|OVADR|OVATT|END
        dta SCR_H-1             ; 200 lines
        dta $00,$00,$00         ; framebuffer at VRAM $000000
        dta $40,$01             ; stride 320
        dta $11,$FF
xdl_len = * - xdl_data

.proc enable_display
        lda #VC_XDL_ON+VC_XCOLOR
        sta VBXE_VCTL
        lda #$00                ; XDL at $07F000
        sta VBXE_XDL0
        lda #$F0
        sta VBXE_XDL1
        lda #$07
        sta VBXE_XDL2
        rts
.endp

; load_pal: (index, r, g, b) quads into VBXE palette #1, terminated by $FF.
;   Writing the blue register auto-advances CSEL, but we set it per colour anyway
;   because the table is sparse.
.proc load_pal
        lda #1
        sta VBXE_PSEL
        ldx #0
?l      lda pal_tab,x
        cmp #$FF
        beq ?done
        sta VBXE_CSEL
        lda pal_tab+1,x
        sta VBXE_CR
        lda pal_tab+2,x
        sta VBXE_CG
        lda pal_tab+3,x
        sta VBXE_CB
        txa
        clc
        adc #4
        tax
        jmp ?l
?done   rts
.endp
pal_tab
        dta 0,   8,  8, 16      ; background
        dta 1, 168,192,240      ; _color+1  arrow
        dta 2, 120,148,208      ; _color+2  bevel light
        dta 3,  84,112,180      ; _color+3  (invert pivot)
        dta 4,  52, 76,144      ; _color+4  face
        dta 5,  28, 44,104      ; _color+5  bevel dark
        dta 6,  20, 32, 80      ; window border
        dta 7,  12, 20, 52      ; window face
        dta 8, 236,236,236      ; text
        dta 9, 216,180, 80      ; title
        dta 10,248,208, 96      ; value
        dta 11,120,120,140      ; hint
        dta $FF

;=============================================================================
; font_expand - OS ROM charset -> an 8-bit "mask" font sheet in VRAM $038000.
;   ASCII 32..95 maps to Atari internal codes 0..63 by simply subtracting 32, so
;   glyph gi lives at CHARSET + gi*8 (8 bytes, 1 bit per pixel, MSB = leftmost).
;   Each is expanded to 8x8 bytes of 255/0 and padded to a 64-byte cell, so the
;   blitter's source address is base + (gi<<6) with no multiply. 64 cells x 64 B
;   is exactly one 4K bank, which is why the whole sheet fits one window select.
;=============================================================================
rowi    dta 0
dsti    dta 0
bits    dta 0

.proc font_expand
        lda #BANK_EN+FONT_BANK
        sta VBXE_BANK_SEL
        lda #<CHARSET
        sta srcp
        lda #>CHARSET
        sta srcp+1
        lda #<MEMW
        sta dstp
        lda #>MEMW
        sta dstp+1
        ldx #0                  ; glyph counter
?glyph  lda #0
        sta rowi
        sta dsti
?row    ldy rowi
        lda (srcp),y
        sta bits
        ldy #8                  ; 8 pixels, MSB first
?col    asl bits
        lda #0
        bcc ?zero
        lda #255                ; a set bit becomes 255 -> blitter AND=colour tints it
?zero   sty ?sy
        ldy dsti
        sta (dstp),y
        ldy ?sy
        inc dsti
        dey
        bne ?col
        inc rowi
        lda rowi
        cmp #8
        bne ?row
        lda srcp                ; next glyph: source +8, destination +64
        clc
        adc #8
        sta srcp
        bcc ?nc
        inc srcp+1
?nc     lda dstp
        clc
        adc #64
        sta dstp
        bcc ?nd
        inc dstp+1
?nd     inx
        cpx #64
        bne ?glyph
        lda #0
        sta VBXE_BANK_SEL
        rts
?sy     dta 0
.endp

;=============================================================================
; VBXE blitter. One blitter control block at VRAM $07F100; do_blit refills it and
;   pulls the trigger. Fills are just blits with AND=0 and XOR=colour.
;=============================================================================
bl_src  dta 0,0,0
bl_ssy  dta a(0)
bl_ssx  dta 0
bl_dst  dta 0,0,0
bl_dsy  dta a(0)
bl_dsx  dta 0
bl_w    dta a(0)                ; width-1
bl_h    dta 0                   ; height-1
bl_and  dta 0
bl_xor  dta 0
bl_mode dta 0                   ; 0 = opaque, 1 = transparent (source 0 = leave alone)

.proc blit_init                 ; point the blitter at the BCB once; it never moves
        lda #<BCB_OFF
        sta VBXE_BL_ADR0
        lda #$F1
        sta VBXE_BL_ADR1
        lda #$07
        sta VBXE_BL_ADR2
        rts
.endp

.proc wait_blit
?w      lda VBXE_BLITTER
        bne ?w
        rts
.endp

.proc do_blit
        jsr wait_blit
        lda #BANK_EN+BANK_XDL
        sta VBXE_BANK_SEL
        lda bl_src
        sta MEMW+BCB_OFF+0
        lda bl_src+1
        sta MEMW+BCB_OFF+1
        lda bl_src+2
        sta MEMW+BCB_OFF+2
        lda bl_ssy
        sta MEMW+BCB_OFF+3
        lda bl_ssy+1
        sta MEMW+BCB_OFF+4
        lda bl_ssx
        sta MEMW+BCB_OFF+5
        lda bl_dst
        sta MEMW+BCB_OFF+6
        lda bl_dst+1
        sta MEMW+BCB_OFF+7
        lda bl_dst+2
        sta MEMW+BCB_OFF+8
        lda bl_dsy
        sta MEMW+BCB_OFF+9
        lda bl_dsy+1
        sta MEMW+BCB_OFF+10
        lda bl_dsx
        sta MEMW+BCB_OFF+11
        lda bl_w
        sta MEMW+BCB_OFF+12
        lda bl_w+1
        sta MEMW+BCB_OFF+13
        lda bl_h
        sta MEMW+BCB_OFF+14
        lda bl_and
        sta MEMW+BCB_OFF+15
        lda bl_xor
        sta MEMW+BCB_OFF+16
        lda #0
        sta MEMW+BCB_OFF+17     ; collision mask / zoom / pattern: unused
        sta MEMW+BCB_OFF+18
        sta MEMW+BCB_OFF+19
        lda bl_mode
        sta MEMW+BCB_OFF+20
        lda #1
        sta VBXE_BLITTER        ; go
        rts
.endp

; calc_addr: calc_out = calc_y*320 + calc_x, as a 24-bit VRAM address in bank 0.
;   y*320 = (y + y>>2)<<8 + (y&3)<<6, which needs no multiply. Preserves Y.
calc_x  dta a(0)
calc_y  dta 0
.proc calc_addr
        lda calc_y
        lsr
        lsr
        clc
        adc calc_y              ; high byte = y + y>>2
        sta ?hi
        lda calc_y
        and #3
        tax
        lda t64,x               ; low bits = (y&3)<<6
        clc
        adc calc_x
        sta calc_out
        lda ?hi
        adc calc_x+1
        sta calc_out+1
        lda #0
        sta calc_out+2
        rts
?hi     dta 0
.endp
t64     dta 0,64,128,192

.proc set_dst_calc
        lda calc_out
        sta bl_dst
        lda calc_out+1
        sta bl_dst+1
        lda calc_out+2
        sta bl_dst+2
        lda #<SCR_W
        sta bl_dsy
        lda #>SCR_W
        sta bl_dsy+1
        lda #1
        sta bl_dsx
        rts
.endp

; fill_rect: calc_x/calc_y = top-left, fr_w x fr_h, colour fr_col. Preserves Y.
fr_w    dta a(0)
fr_h    dta 0
fr_col  dta 0
.proc fill_rect
        jsr calc_addr
        jsr set_dst_calc
        lda #0                  ; a constant source: the blitter reads nothing
        sta bl_src
        sta bl_src+1
        sta bl_src+2
        sta bl_ssy
        sta bl_ssy+1
        lda #1
        sta bl_ssx
        lda fr_w
        sec
        sbc #1
        sta bl_w
        lda fr_w+1
        sbc #0
        sta bl_w+1
        lda fr_h
        sec
        sbc #1
        sta bl_h
        lda #0
        sta bl_and              ; AND=0, XOR=colour -> every output byte = colour
        lda fr_col
        sta bl_xor
        lda #0
        sta bl_mode
        jmp do_blit
.endp

;=============================================================================
; text
;=============================================================================
text_x   dta a(0)
text_y   dta 0
text_col dta 0

; draw_char: A = glyph index (ASCII-32). Source = $038000 + (gi<<6), i.e.
;   low byte (gi&3)<<6, high byte $80 + (gi>>2). Advances text_x by 8.
.proc draw_char
        sta ?gi
        lda ?gi
        and #3
        asl
        asl
        asl
        asl
        asl
        asl                     ; (gi&3) * 64
        sta bl_src
        lda ?gi
        lsr
        lsr                     ; gi>>2
        clc
        adc #FONT_HI
        sta bl_src+1
        lda #FONT_B2
        sta bl_src+2
        lda #8                  ; the sheet's rows are 8 bytes apart
        sta bl_ssy
        lda #0
        sta bl_ssy+1
        lda #1
        sta bl_ssx
        lda text_x
        sta calc_x
        lda text_x+1
        sta calc_x+1
        lda text_y
        sta calc_y
        jsr calc_addr
        jsr set_dst_calc
        lda #7                  ; 8x8 glyph
        sta bl_w
        lda #0
        sta bl_w+1
        lda #7
        sta bl_h
        lda text_col
        sta bl_and              ; 255 & colour = colour, 0 & colour = 0
        lda #0
        sta bl_xor
        lda #1
        sta bl_mode             ; transparent: an output byte of 0 leaves the pixel
        jsr do_blit
        lda text_x              ; fixed 8px advance (the game's font is proportional
        clc                     ;   and carries a width table; the ROM charset is not)
        adc #8
        sta text_x
        bcc ?nc
        inc text_x+1
?nc     rts
?gi     dta 0
.endp

; draw_text: txt_ptr -> a 0-terminated uppercase string. Anything outside 32..95
;   is skipped -- the ROM's first 64 internal codes cover exactly that range.
.proc draw_text
        ldy #0
?l      lda text_x+1            ; right-edge clip: a glyph that would run past x=319
        beq ?nclip              ;   wraps onto the next row and ghosts there
        lda text_x
        cmp #<312
        bcs ?done
?nclip  lda (txt_ptr),y
        beq ?done
        sec
        sbc #32
        bcc ?skip
        cmp #64
        bcs ?skip
        sty ?sy
        jsr draw_char
        ldy ?sy
?skip   iny
        bne ?l
?done   rts
?sy     dta 0
.endp

; text_at: A/X = string lo/hi, Y = colour. Caller sets text_x / text_y.
.proc text_at
        sta txt_ptr
        stx txt_ptr+1
        sty text_col
        jmp draw_text
.endp

;=============================================================================
; the ArrowButton widget - OpenXcom ArrowButton::draw(), transcribed.
;   Every entry below is one drawRect/setPixel call from ArrowButton.cpp, given
;   as (dx, dy, w, h, colour-offset) relative to the button's top-left. The offset
;   is added to the widget's base colour, which here is 0, so it indexes the ramp
;   directly. $FF ends a list.
;=============================================================================
awp_x     dta 0
awp_y     dta 0
awp_shape dta 0                  ; 0 = ARROW_BIG_UP, 1 = ARROW_BIG_DOWN
awp_press dta 0                  ; 1 = invert(_color+3), the held-down look

box_ops                         ; ArrowButton.cpp:99-124, the bevelled box
        dta 0,0,AW_W-1,AW_H-1,2 ; :103-108  drawRect(_color+2)
        dta 1,1,AW_W-1,AW_H-1,5 ; :110-114  drawRect(_color+5)
        dta 1,1,AW_W-2,AW_H-2,4 ; :116-120  drawRect(_color+4)
        dta 0,0,1,1,1           ; :122      setPixel(0, 0, _color+1)
        dta 0,AW_H-1,1,1,4      ; :123      setPixel(0, h-1, _color+4)
        dta AW_W-1,0,1,1,4      ; :124      setPixel(w-1, 0, _color+4)
        dta $FF
up_ops                          ; :130-152  ARROW_BIG_UP
        dta 5,8,3,3,1           ; the 3x3 stem
        dta 2,7,9,1,1           ; then the triangle, 9px wide, narrowing upward
        dta 3,6,7,1,1
        dta 4,5,5,1,1
        dta 5,4,3,1,1
        dta 6,3,1,1,1
        dta $FF
dn_ops                          ; :153-175  ARROW_BIG_DOWN
        dta 5,3,3,3,1
        dta 2,6,9,1,1
        dta 3,7,7,1,1
        dta 4,8,5,1,1
        dta 5,9,3,1,1
        dta 6,10,1,1,1
        dta $FF

; aw_col: A = colour offset (1..5) -> the palette index to draw with. Pressed is
;   Surface::invert(mid) with mid = _color+3, i.e. p -> 2*mid - p, which for
;   _color+N collapses to _color+(6-N).
.proc aw_col
        ldx awp_press
        beq ?plain
        sta ?t
        lda #6
        sec
        sbc ?t
?plain  rts
?t      dta 0
.endp

; aw_run: walk one op list, filling each rectangle at the widget's origin.
.proc aw_run
        ldy #0
?l      lda (opp),y
        cmp #$FF
        beq ?done
        clc
        adc awp_x
        sta calc_x
        lda #0
        sta calc_x+1
        iny
        lda (opp),y
        clc
        adc awp_y
        sta calc_y
        iny
        lda (opp),y
        sta fr_w
        lda #0
        sta fr_w+1
        iny
        lda (opp),y
        sta fr_h
        iny
        lda (opp),y
        sty ?sy
        jsr aw_col
        sta fr_col
        jsr fill_rect
        ldy ?sy
        iny
        jmp ?l
?done   rts
?sy     dta 0
.endp

; aw_draw: the box, then the shape. Two op lists, one walker -- exactly the
;   structure of ArrowButton::draw()'s "draw button, then switch (_shape)".
.proc aw_draw
        lda #<box_ops
        sta opp
        lda #>box_ops
        sta opp+1
        jsr aw_run
        lda awp_shape
        bne ?dn
        lda #<up_ops
        sta opp
        lda #>up_ops
        sta opp+1
        jmp aw_run
?dn     lda #<dn_ops
        sta opp
        lda #>dn_ops
        sta opp+1
        jmp aw_run
.endp

;=============================================================================
; the screen
;=============================================================================
.proc draw_screen
        lda #0                  ; clear the framebuffer
        sta calc_x
        sta calc_x+1
        sta calc_y
        sta fr_col
        lda #<SCR_W
        sta fr_w
        lda #>SCR_W
        sta fr_w+1
        lda #SCR_H
        sta fr_h
        jsr fill_rect
        lda #WIN_X              ; window: a 2px border...
        sta calc_x
        lda #0
        sta calc_x+1
        lda #WIN_Y
        sta calc_y
        lda #WIN_W
        sta fr_w
        lda #0
        sta fr_w+1
        lda #WIN_H
        sta fr_h
        lda #C_BORDER
        sta fr_col
        jsr fill_rect
        lda #WIN_X+2            ; ...around an inset face. (The game blits a crop of
        sta calc_x              ;   the original BACK*.SCR artwork in here instead.)
        lda #0
        sta calc_x+1
        lda #WIN_Y+2
        sta calc_y
        lda #WIN_W-4
        sta fr_w
        lda #0
        sta fr_w+1
        lda #WIN_H-4
        sta fr_h
        lda #C_WIN
        sta fr_col
        jsr fill_rect
        lda #TXT_X              ; title
        sta text_x
        lda #0
        sta text_x+1
        lda #TITLE_Y
        sta text_y
        lda #<s_title
        ldx #>s_title
        ldy #C_TITLE
        jsr text_at
        lda #TXT_X              ; label
        sta text_x
        lda #0
        sta text_x+1
        lda #LABEL_Y
        sta text_y
        lda #<s_label
        ldx #>s_label
        ldy #C_TEXT
        jsr text_at
        lda #HINT_X             ; hint, outside the window
        sta text_x
        lda #0
        sta text_x+1
        lda #HINT_Y
        sta text_y
        lda #<s_hint
        ldx #>s_hint
        ldy #C_HINT
        jsr text_at
        jsr draw_num
        ; fall into draw_arrows
.endp

; draw_arrows: both widgets, the one under the stick drawn pressed.
.proc draw_arrows
        lda #AW_X
        sta awp_x
        lda #AW_UP_Y
        sta awp_y
        lda #0
        sta awp_shape
        ldx #0                  ; held == 0 -> the up widget draws pressed
        lda held
        bne ?up
        inx
?up     stx awp_press
        jsr aw_draw
        lda #AW_X
        sta awp_x
        lda #AW_DN_Y
        sta awp_y
        lda #1
        sta awp_shape
        ldx #0                  ; held == 1 -> the down widget draws pressed
        lda held
        cmp #1
        bne ?dn
        inx
?dn     stx awp_press
        jmp aw_draw
.endp

; draw_num: repaint just the two digits. Wiping and redrawing a 16x8 strip beats
;   redrawing the window every time the value ticks -- that is what makes the
;   auto-repeat look solid instead of flickering.
.proc draw_num
        lda #VAL_X
        sta calc_x
        lda #0
        sta calc_x+1
        lda #LABEL_Y
        sta calc_y
        lda #16
        sta fr_w
        lda #0
        sta fr_w+1
        lda #8
        sta fr_h
        lda #C_WIN
        sta fr_col
        jsr fill_rect
        lda #VAL_X
        sta text_x
        lda #0
        sta text_x+1
        lda #LABEL_Y
        sta text_y
        lda #C_VALUE
        sta text_col
        ldx #0                  ; split value into tens and units
        lda value
?t      cmp #10
        bcc ?ones
        sec
        sbc #10
        inx
        jmp ?t
?ones   sta ?u
        txa
        clc
        adc #16                 ; '0' is ASCII 48, so glyph index 16
        jsr draw_char
        lda ?u
        clc
        adc #16
        jmp draw_char
?u      dta 0
.endp

s_title dta c'ALLOCATE SCIENTISTS',0
s_label dta c'SCIENTISTS>',0
s_hint  dta c'STICK UP/DOWN TO CHANGE',0

        run main
