tungsten/tungsten.tal

1818 lines
81 KiB
Tal

( ---------------------------------------------------------------------------- )
( I N S T R U C T I O N R E N A M I N G M A C R O S )
%LIT* { LIT2 } %LITr* { LIT2r } %LITk* { LIT2k } %LITkr* { LIT2kr }
%INC* { INC2 } %INCr* { INC2r } %INCk* { INC2k } %INCkr* { INC2kr }
%POP* { POP2 } %POPr* { POP2r } %POPk* { POP2k } %POPkr* { POP2kr }
%NIP* { NIP2 } %NIPr* { NIP2r } %NIPk* { NIP2k } %NIPkr* { NIP2kr }
%SWP* { SWP2 } %SWPr* { SWP2r } %SWPk* { SWP2k } %SWPkr* { SWP2kr }
%ROT* { ROT2 } %ROTr* { ROT2r } %ROTk* { ROT2k } %ROTkr* { ROT2kr }
%DUP* { DUP2 } %DUPr* { DUP2r } %DUPk* { DUP2k } %DUPkr* { DUP2kr }
%OVR* { OVR2 } %OVRr* { OVR2r } %OVRk* { OVR2k } %OVRkr* { OVR2kr }
%EQU* { EQU2 } %EQUr* { EQU2r } %EQUk* { EQU2k } %EQUkr* { EQU2kr }
%NEQ* { NEQ2 } %NEQr* { NEQ2r } %NEQk* { NEQ2k } %NEQkr* { NEQ2kr }
%GTH* { GTH2 } %GTHr* { GTH2r } %GTHk* { GTH2k } %GTHkr* { GTH2kr }
%LTH* { LTH2 } %LTHr* { LTH2r } %LTHk* { LTH2k } %LTHkr* { LTH2kr }
%JMP* { JMP2 } %JMPr* { JMP2r } %JMPk* { JMP2k } %JMPkr* { JMP2kr }
%JCN* { JCN2 } %JCNr* { JCN2r } %JCNk* { JCN2k } %JCNkr* { JCN2kr }
%JSR* { JSR2 } %JSRr* { JSR2r } %JSRk* { JSR2k } %JSRkr* { JSR2kr }
%STH* { STH2 } %STHr* { STH2r } %STHk* { STH2k } %STHkr* { STH2kr }
%LDZ* { LDZ2 } %LDZr* { LDZ2r } %LDZk* { LDZ2k } %LDZkr* { LDZ2kr }
%STZ* { STZ2 } %STZr* { STZ2r } %STZk* { STZ2k } %STZkr* { STZ2kr }
%LDR* { LDR2 } %LDRr* { LDR2r } %LDRk* { LDR2k } %LDRkr* { LDR2kr }
%STR* { STR2 } %STRr* { STR2r } %STRk* { STR2k } %STRkr* { STR2kr }
%LDA* { LDA2 } %LDAr* { LDA2r } %LDAk* { LDA2k } %LDAkr* { LDA2kr }
%STA* { STA2 } %STAr* { STA2r } %STAk* { STA2k } %STAkr* { STA2kr }
%DEI* { DEI2 } %DEIr* { DEI2r } %DEIk* { DEI2k } %DEIkr* { DEI2kr }
%DEO* { DEO2 } %DEOr* { DEO2r } %DEOk* { DEO2k } %DEOkr* { DEO2kr }
%ADD* { ADD2 } %ADDr* { ADD2r } %ADDk* { ADD2k } %ADDkr* { ADD2kr }
%SUB* { SUB2 } %SUBr* { SUB2r } %SUBk* { SUB2k } %SUBkr* { SUB2kr }
%MUL* { MUL2 } %MULr* { MUL2r } %MULk* { MUL2k } %MULkr* { MUL2kr }
%DIV* { DIV2 } %DIVr* { DIV2r } %DIVk* { DIV2k } %DIVkr* { DIV2kr }
%AND* { AND2 } %ANDr* { AND2r } %ANDk* { AND2k } %ANDkr* { AND2kr }
%ORA* { ORA2 } %ORAr* { ORA2r } %ORAk* { ORA2k } %ORAkr* { ORA2kr }
%EOR* { EOR2 } %EORr* { EOR2r } %EORk* { EOR2k } %EORkr* { EOR2kr }
%SFT* { SFT2 } %SFTr* { SFT2r } %SFTk* { SFT2k } %SFTkr* { SFT2kr }
( ---------------------------------------------------------------------------- )
( D E V I C E M A C R O S )
( Device ports )
%/SYSTEM.WST { LIT 02 } %r/SYSTEM.WST { LITr 02 }
%/SYSTEM.RST { LIT 03 } %r/SYSTEM.RST { LITr 03 }
%/SYSTEM.RED { LIT 08 } %r/SYSTEM.RED { LITr 08 }
%/SYSTEM.GREEN { LIT 0a } %r/SYSTEM.GREEN { LITr 0a }
%/SYSTEM.BLUE { LIT 0c } %r/SYSTEM.BLUE { LITr 0c }
%/SYSTEM.DEBUG { LIT 0e } %r/SYSTEM.DEBUG { LITr 0e }
%/SYSTEM.HALT { LIT 0f } %r/SYSTEM.HALT { LITr 0f }
%/CONSOLE.VECTOR { LIT 10 } %r/CONSOLE.VECTOR { LITr 10 }
%/CONSOLE.READ { LIT 12 } %r/CONSOLE.READ { LITr 12 }
%/CONSOLE.WRITE { LIT 18 } %r/CONSOLE.WRITE { LITr 18 }
%/CONSOLE.ERROR { LIT 19 } %r/CONSOLE.ERROR { LITr 19 }
%/SCREEN.VECTOR { LIT 20 } %r/SCREEN.VECTOR { LITr 20 }
%/SCREEN.WIDTH { LIT 22 } %r/SCREEN.WIDTH { LITr 22 }
%/SCREEN.HEIGHT { LIT 24 } %r/SCREEN.HEIGHT { LITr 24 }
%/SCREEN.AUTO { LIT 26 } %r/SCREEN.AUTO { LITr 26 }
%/SCREEN.X { LIT 28 } %r/SCREEN.X { LITr 28 }
%/SCREEN.Y { LIT 2a } %r/SCREEN.Y { LITr 2a }
%/SCREEN.ADDR { LIT 2c } %r/SCREEN.ADDR { LITr 2c }
%/SCREEN.PIXEL { LIT 2e } %r/SCREEN.PIXEL { LITr 2e }
%/SCREEN.SPRITE { LIT 2f } %r/SCREEN.SPRITE { LITr 2f }
%/CONTROLLER.VECTOR { LIT 80 } %r/CONTROLLER.VECTOR { LITr 80 }
%/CONTROLLER.BUTTON { LIT 82 } %r/CONTROLLER.BUTTON { LITr 82 }
%/CONTROLLER.KEY { LIT 83 } %r/CONTROLLER.KEY { LITr 83 }
%/MOUSE.VECTOR { LIT 90 } %r/MOUSE.VECTOR { LITr 90 }
%/MOUSE.X { LIT 92 } %r/MOUSE.X { LITr 92 }
%/MOUSE.Y { LIT 94 } %r/MOUSE.Y { LITr 94 }
%/MOUSE.STATE { LIT 96 } %r/MOUSE.STATE { LITr 96 }
%/MOUSE.SCROLLX { LIT 9a } %r/MOUSE.SCROLLX { LITr 9a }
%/MOUSE.SCROLLY { LIT 9c } %r/MOUSE.SCROLLY { LITr 9c }
( Read from device ports )
%/SYSTEM.WST? { /SYSTEM.WST DEI } %r/SYSTEM.WST? { r/SYSTEM.WST DEIr }
%/SYSTEM.RST? { /SYSTEM.RST DEI } %r/SYSTEM.RST? { r/SYSTEM.RST DEIr }
%/CONSOLE.READ? { /CONSOLE.READ DEI } %r/CONSOLE.READ? { r/CONSOLE.READ DEIr }
%/SCREEN.WIDTH? { /SCREEN.WIDTH DEI* } %r/SCREEN.WIDTH? { r/SCREEN.WIDTH DEIr* }
%/SCREEN.HEIGHT? { /SCREEN.HEIGHT DEI* } %r/SCREEN.HEIGHT? { r/SCREEN.HEIGHT DEIr* }
%/SCREEN.AUTO? { /SCREEN.AUTO DEI } %r/SCREEN.AUTO? { r/SCREEN.AUTO DEIr }
%/SCREEN.X? { /SCREEN.X DEI* } %r/SCREEN.X? { r/SCREEN.X DEIr* }
%/SCREEN.Y? { /SCREEN.Y DEI* } %r/SCREEN.Y? { r/SCREEN.Y DEIr* }
%/SCREEN.ADDR? { /SCREEN.ADDR DEI* } %r/SCREEN.ADDR? { r/SCREEN.ADDR DEIr* }
%/CONTROLLER.BUTTON? { /CONTROLLER.BUTTON DEI } %r/CONTROLLER.BUTTON? { r/CONTROLLER.BUTTON DEIr }
%/CONTROLLER.KEY? { /CONTROLLER.KEY DEI } %r/CONTROLLER.KEY? { r/CONTROLLER.KEY DEIr }
%/MOUSE.X? { /MOUSE.X DEI* } %r/MOUSE.X? { r/MOUSE.X DEIr* }
%/MOUSE.Y? { /MOUSE.Y DEI* } %r/MOUSE.Y? { r/MOUSE.Y DEIr* }
%/MOUSE.STATE? { /MOUSE.STATE DEI } %r/MOUSE.STATE? { r/MOUSE.STATE DEIr }
%/MOUSE.SCROLLX? { /MOUSE.SCROLLX DEI } %r/MOUSE.SCROLLX? { r/MOUSE.SCROLLX DEIr }
%/MOUSE.SCROLLY? { /MOUSE.SCROLLY DEI } %r/MOUSE.SCROLLY? { r/MOUSE.SCROLLY DEIr }
( Write to device ports )
%/SYSTEM.WST! { /SYSTEM.WST DEO } %r/SYSTEM.WST! { r/SYSTEM.WST DEOr }
%/SYSTEM.RST! { /SYSTEM.RST DEO } %r/SYSTEM.RST! { r/SYSTEM.RST DEOr }
%/SYSTEM.RED! { /SYSTEM.RED DEO* } %r/SYSTEM.RED! { r/SYSTEM.RED DEOr* }
%/SYSTEM.GREEN! { /SYSTEM.GREEN DEO* } %r/SYSTEM.GREEN! { r/SYSTEM.GREEN DEOr* }
%/SYSTEM.BLUE! { /SYSTEM.BLUE DEO* } %r/SYSTEM.BLUE! { r/SYSTEM.BLUE DEOr* }
%/SYSTEM.DEBUG! { /SYSTEM.DEBUG DEO } %r/SYSTEM.DEBUG! { r/SYSTEM.DEBUG DEOr }
%/SYSTEM.HALT! { /SYSTEM.HALT DEO } %r/SYSTEM.HALT! { r/SYSTEM.HALT DEOr }
%/CONSOLE.VECTOR! { /CONSOLE.VECTOR DEO* } %r/CONSOLE.VECTOR! { r/CONSOLE.VECTOR DEOr* }
%/CONSOLE.WRITE! { /CONSOLE.WRITE DEO } %r/CONSOLE.WRITE! { r/CONSOLE.WRITE DEOr }
%/CONSOLE.ERROR! { /CONSOLE.ERROR DEO } %r/CONSOLE.ERROR! { r/CONSOLE.ERROR DEOr }
%/SCREEN.VECTOR! { /SCREEN.VECTOR DEO* } %r/SCREEN.VECTOR! { r/SCREEN.VECTOR DEOr* }
%/SCREEN.WIDTH! { /SCREEN.WIDTH DEO* } %r/SCREEN.WIDTH! { r/SCREEN.WIDTH DEOr* }
%/SCREEN.HEIGHT! { /SCREEN.HEIGHT DEO* } %r/SCREEN.HEIGHT! { r/SCREEN.HEIGHT DEOr* }
%/SCREEN.AUTO! { /SCREEN.AUTO DEO } %r/SCREEN.AUTO! { r/SCREEN.AUTO DEOr }
%/SCREEN.X! { /SCREEN.X DEO* } %r/SCREEN.X! { r/SCREEN.X DEOr* }
%/SCREEN.Y! { /SCREEN.Y DEO* } %r/SCREEN.Y! { r/SCREEN.Y DEOr* }
%/SCREEN.ADDR! { /SCREEN.ADDR DEO* } %r/SCREEN.ADDR! { r/SCREEN.ADDR DEOr* }
%/SCREEN.PIXEL! { /SCREEN.PIXEL DEO } %r/SCREEN.PIXEL! { r/SCREEN.PIXEL DEOr }
%/SCREEN.SPRITE! { /SCREEN.SPRITE DEO } %r/SCREEN.SPRITE! { r/SCREEN.SPRITE DEOr }
%/CONTROLLER.VECTOR! { /CONTROLLER.VECTOR DEO* } %r/CONTROLLER.VECTOR! { r/CONTROLLER.VECTOR DEOr* }
%/MOUSE.VECTOR! { /MOUSE.VECTOR DEO* } %r/MOUSE.VECTOR! { r/MOUSE.VECTOR DEOr* }
( ---------------------------------------------------------------------------- )
( C O N S T A N T S M A C R O S )
%BUTTON_MASK_UP { #10 } %BUTTON_MASK_A { #01 }
%BUTTON_MASK_DOWN { #20 } %BUTTON_MASK_B { #02 }
%BUTTON_MASK_LEFT { #40 } %BUTTON_MASK_SELECT { #04 }
%BUTTON_MASK_RIGHT { #80 } %BUTTON_MASK_START { #08 }
%MOUSE_MASK_1 { #01 } %MOUSE_MASK_5 { #10 }
%MOUSE_MASK_2 { #02 } %MOUSE_MASK_6 { #20 }
%MOUSE_MASK_3 { #04 } %MOUSE_MASK_7 { #40 }
%MOUSE_MASK_4 { #08 } %MOUSE_MASK_8 { #80 }
( Colour mappings for pixel colours, 1-bit sprite colours, and 2-bit sprite colours. )
%COL_0 { #00 } %COL_00 { #00 } %COL_0012 { #00 }
%COL_1 { #01 } %COL_01 { #01 } %COL_0123 { #01 }
%COL_2 { #02 } %COL_02 { #02 } %COL_0231 { #02 }
%COL_3 { #03 } %COL_03 { #03 } %COL_0312 { #03 }
%COL_10 { #04 } %COL_1012 { #04 }
%COL_T1 { #05 } %COL_T123 { #05 }
%COL_12 { #06 } %COL_1231 { #06 }
%COL_13 { #07 } %COL_1312 { #07 }
%COL_20 { #08 } %COL_2012 { #08 }
%COL_21 { #09 } %COL_2123 { #09 }
%COL_T2 { #0a } %COL_T231 { #0a }
%COL_23 { #0b } %COL_2312 { #0b }
%COL_30 { #0c } %COL_3012 { #0c }
%COL_31 { #0d } %COL_3123 { #0d }
%COL_32 { #0e } %COL_3231 { #0e }
%COL_T3 { #0f } %COL_T312 { #0f }
( ---------------------------------------------------------------------------- )
( I N S T R U C T I O N M A C R O S )
%MOD { DIVk MUL SUB } ( val mod -- remainder )
%MODr { DIVkr MULr SUBr } ( val mod -- remainder )
%MOD* { DIVk* MUL* SUB* } ( val* mod* -- remainder* )
%MODr* { DIVkr* MULr* SUBr* } ( val* mod* -- remainder* )
%MOD2 { #01 AND }
%MOD2* { #0001 AND* }
%MOD4 { #03 AND }
%MOD4* { #0003 AND* }
%MOD8 { #07 AND }
%MOD8* { #0007 AND* }
%DIVMOD { DIVk MULk STH ROT STHr SUB ROT POP } ( val mod -- quotient remainder )
%DIVMOD* { DIVk* MULk* STH* ROT* STHr* SUB* ROT* POP* } ( val* mod* -- quotient* remainder* )
%GTH0 { }
%GTH0* { ADD }
%ADD2* { INC* INC* }
%DOUBLE { DUP ADD }
%DOUBLE* { DUP* ADD* }
%MUL4 { #20 SFT }
%MUL4* { #20 SFT* }
%MUL8 { #30 SFT }
%MUL8* { #30 SFT* }
%HALVE { #01 SFT }
%HALVE* { #01 SFT* }
%DIV4 { #02 SFT }
%DIV4* { #02 SFT* }
%DIV8 { #03 SFT }
%DIV8* { #03 SFT* }
%SHL1 { #10 SFT } %SHR1 { #01 SFT }
%SHL2 { #20 SFT } %SHR2 { #02 SFT }
%SHL3 { #30 SFT } %SHR3 { #03 SFT }
%SHL4 { #40 SFT } %SHR4 { #04 SFT }
%SHL5 { #50 SFT } %SHR5 { #05 SFT }
%SHL6 { #60 SFT } %SHR6 { #06 SFT }
%SHL7 { #70 SFT } %SHR7 { #07 SFT }
%SHL8 { #80 SFT } %SHR8 { #08 SFT }
%SHL1* { #10 SFT* } %SHR1* { #01 SFT* }
%SHL2* { #20 SFT* } %SHR2* { #02 SFT* }
%SHL3* { #30 SFT* } %SHR3* { #03 SFT* }
%SHL4* { #40 SFT* } %SHR4* { #04 SFT* }
%SHL5* { #50 SFT* } %SHR5* { #05 SFT* }
%SHL6* { #60 SFT* } %SHR6* { #06 SFT* }
%SHL7* { #70 SFT* } %SHR7* { #07 SFT* }
%SHL8* { #80 SFT* } %SHR8* { #08 SFT* }
%FLOOR8 { #07 EOR }
%FLOOR8* { #0007 EOR* }
%CEIL8 { #07 ADD FLOOR8 }
%CEIL8* { #0007 ADD* FLOOR8* }
%CEIL8DIV8 { #07 ADD DIV8 }
%CEIL8DIV8* { #0007 ADD* DIV8* }
%DEC { #01 SUB }
%DECr { LITr 01 SUBr }
%DECk { DUP #01 SUB }
%DECkr { DUPr LITr 01 SUBr }
%DEC* { #0001 SUB* }
%DECr* { LITr* 0001 SUBr* }
%DECk* { DUP* #0001 SUB* }
%DECkr* { DUPr* LITr* 0001 SUBr* }
%CALL { JSR* }
%GOTO { JMP* }
%RETURN { JMPr* }
%NORETURN { POPr* } ( for removing a return address from the return stack )
%CALL_RET { GOTO }
( If greater than 0, call the address. If not, skip the call and pop the address. )
( bool addr* -- )
%CALL_IF { OVR NOT DOUBLE JMP CALL DUPk POP* POP }
%IS_POSITIVE { #80 LTH }
%IS_POSITIVE* { #8000 LTH* }
%IS_NEGATIVE { #7f GTH }
%IS_NEGATIVE* { #7fff GTH* }
%NEGATE { #00 SWP SUB }
%NEGATE* { #0000 SWP* SUB* }
%ABS* { DUP* IS_POS* #05 JCN NEG* }
%TO_SHORT { #00 SWP }
%DIGIT_TO_ASCII { #30 ADD }
%CODE_TO_ASCII { #20 ADD }
%ASCII_TO_CODE { #20 SUB }
( Convert between pixel colours [0,1,2,3] and sprite foreground colours [0,4,8,c] )
%TO_SPRITE_COLOUR { SHL2 }
%TO_PIXEL_COLOUR { SHR2 }
%<NULL> { 00 }
%<SPACE> { 20 }
%<LINEFEED> { 0a }
%CHOOSE { ROT JMP SWP NIP } ( flag value0 value1 -- value )
%CHOOSE* { STH* ROT STHr* ROT JMP SWP* NIP* } ( flag value0* value1* -- value* )
%MASK { SWP OVR AND EQU } ( val mask -- bool )
%MASK* { SWP* OVR* AND* EQU* } ( val* mask* -- bool )
%MOVE_LEFT { /SCREEN.X? SWP* SUB* /SCREEN.X! }
%MOVE_RIGHT { /SCREEN.X? ADD* /SCREEN.X! }
%MOVE_UP { /SCREEN.Y? SWP* SUB* /SCREEN.Y! }
%MOVE_DOWN { /SCREEN.Y? ADD* /SCREEN.Y! }
%MOVE_LEFT_1 { /SCREEN.X? DEC* /SCREEN.X! }
%MOVE_RIGHT_1 { /SCREEN.X? INC* /SCREEN.X! }
%MOVE_UP_1 { /SCREEN.Y? DEC* /SCREEN.Y! }
%MOVE_DOWN_1 { /SCREEN.Y? INC* /SCREEN.Y! }
%BREAKPOINT { #0101 /SYSTEM.DEBUG DEO* BRK }
%DEBUG { #01 /SYSTEM.DEBUG! }
%HALT { #01 /SYSTEM.HALT! BRK }
%PANIC { BREAKPOINT }
%IF_FALSE { JMP } ( value -- )
%IF_FALSE* { NIP JMP } ( value* -- )
%IF_FALSEY { #01 JCN } ( value -- )
%IF_FALSEY* { ORA #01 JCN } ( value* -- )
%IF_TRUE { IF_TRUTHY }
%IF_TRUE* { IF_TRUTHY* }
%IF_TRUTHY { NOT JMP } ( value -- )
%IF_TRUTHY* { NOT* JMP } ( value -- )
%IF_EQUAL { NEQ JMP } ( value1 value2 -- )
%IF_EQUAL* { NEQ* JMP } ( value1* value2* -- )
%IF_NOT_EQUAL { EQU JMP } ( value1 value2 -- )
%IF_NOT_EQUAL* { EQU* JMP } ( value1* value2* -- )
%IF_MASK { AND IF_TRUTHY } ( value1 value2 -- )
%IF_MASK* { AND* IF_TRUTHY* } ( value1 value2 -- )
%IF_NOT_MASK { AND IF_FALSEY } ( value1 value2 -- )
%IF_NOT_MASK* { AND* IF_FALSEY* } ( value1 value2 -- )
%TO_BOOL { #00 GTH }
%TO_BOOL* { ORA TO_BOOL }
%NOT { #00 EQU }
%NOT* { ORA NOT }
%INC_Z { LDZk INC SWP STZ }
%DEC_Z { LDZk DEC SWP STZ }
%INC_Z* { LDZk* INC* ROT STZ* }
%DEC_Z* { LDZk* DEC* ROT STZ* }
%INC_A { LDAk INC ROT ROT STA }
%DEC_A { LDAk DEC ROT ROT STA }
%INC_A* { LDAk* INC* SWP* STA* }
%DEC_A* { LDAk* DEC* SWP* STA* }
%INC_R { PANIC } ( Not possible to implement for relative addresses )
%DEC_R { PANIC } ( Not possible to implement for relative addresses )
%INC_R* { PANIC } ( Not possible to implement for relative addresses )
%DEC_R* { PANIC } ( Not possible to implement for relative addresses )
%AND_FLIPX { #10 ORA }
%AND_FLIPY { #20 ORA }
%AND_FG { #40 ORA }
%AND_2BIT { #80 ORA }
( ---------------------------------------------------------------------------- )
( G L O B A L V A R I A B L E S )
( Canvas origin in screen-space coords )
%/V.CANVAS.X { #08 } %/V.CANVAS.X? { #08 LDZ* } %/V.CANVAS.X! { #08 STZ* }
%/V.CANVAS.Y { #0a } %/V.CANVAS.Y? { #0a LDZ* } %/V.CANVAS.Y! { #0a STZ* }
( Canvas size )
%/V.CANVAS.WIDTH { #0c } %/V.CANVAS.WIDTH? { #0c LDZ* } %/V.CANVAS.WIDTH! { #0c STZ* }
%/V.CANVAS.HEIGHT { #0e } %/V.CANVAS.HEIGHT? { #0e LDZ* } %/V.CANVAS.HEIGHT! { #0e STZ* }
( Canvas colour mode: 0=1-bit, 1=2-bit )
%/V.CANVAS.MODE { #16 } %/V.CANVAS.MODE? { #16 LDZ } %/V.CANVAS.MODE! { #16 STZ }
( The currently active drawing tool. 0=Pan )
%/V.ACTIVE_TOOL { #17 } %/V.ACTIVE_TOOL? { #17 LDZ } %/V.ACTIVE_TOOL! { #17 STZ }
%PRINTF(\s) { ;print_space CALL }
%PRINTF(\n) { ;print_newline CALL }
%PRINTF(%s) { ;print_string CALL }
%PRINTF(%s\s) { PRINTF(%s) PRINTF(\s) }
%PRINTF(%s\n) { PRINTF(%s) PRINTF(\n) }
%PRINTF(%d) { ;print_byte_decimal CALL }
%PRINTF(%d\s) { PRINTF(%d) PRINTF(\s) }
%PRINTF(%d\n) { PRINTF(%d) PRINTF(\n) }
%PRINTF(%d*) { ;print_short_decimal CALL }
%PRINTF(%d*\s) { PRINTF(%d*) PRINTF(\s) }
%PRINTF(%d*\n) { PRINTF(%d*) PRINTF(\n) }
%PRINTF(%-d*) { ;print_short_decimal_signed CALL }
%PRINTF(%-d*\s) { PRINTF(%-d*) PRINTF(\s) }
%PRINTF(%-d*\n) { PRINTF(%-d*) PRINTF(\n) }
%PRINTF(%b) { ;print_byte_binary CALL }
%PRINTF(%b\s) { PRINTF(%b) PRINTF(\s) }
%PRINTF(%b\n) { PRINTF(%b) PRINTF(\n) }
%PRINTF(%?) { ;print_bool CALL }
%PRINTF(%?\s) { PRINTF(%?) PRINTF(\s) }
%PRINTF(%?\n) { PRINTF(%?) PRINTF(\n) }
%PRINTF(%?*) { ;print_bool_short CALL }
%PRINTF(%?*\s) { PRINTF(%?) PRINTF(\s) }
%PRINTF(%?*\n) { PRINTF(%?) PRINTF(\n) }
( ---------------------------------------------------------------------------- )
( P R O G R A M S T A R T )
|0100
@program_start ( -- )
( #0008 /V.CANVAS.WIDTH!
#0008 /V.CANVAS.HEIGHT!
COL_1 #0001 #0002
;fill_canvas_from_point CALL
HALT
)
( Initialise variables )
#0080 /V.CANVAS.WIDTH!
#0060 /V.CANVAS.HEIGHT!
;colour_palette_ui #04 ;ui__set_colour_palette CALL
;launch_screen GOTO
;canvas_screen GOTO
( ---------------------------------------------------------------------------- )
( C A N V A S P R O C E D U R E S )
( ALL OF THESE ROUTINES USE CANVAS-SPACE COORDINATES )
( Get the colour of the canvas pixel at the given coordinates. )
@get_canvas_pixel_colour ( x* y* -- colour )
;get_mask_and_address CALL LDA AND TO_BOOL RETURN
@set_canvas_pixel_colour ( colour x* y* -- )
;get_mask_and_address CALL STHk* LDA ROT ,&col_1 JCN ( mask byte | row_addr* )
&col_0 EOR ,&end JMP
&col_1 ORA
&end STHr* STA RETURN
@check_in_canvas_bounds ( x* y* -- in_bounds? )
/V.CANVAS.HEIGHT? LTH* STH /V.CANVAS.WIDTH? LTH* STHr AND RETURN
( Return 1 if pixel is in-bounds and the given colour, otherwise return 0 )
@check_canvas_pixel_colour ( colour x* y* -- is_colour? )
OVR* OVR* ;check_in_canvas_bounds CALL ,&cont JCN
( bail out ) POP* POP* POP #00 RETURN
&cont ;get_canvas_pixel_colour CALL EQU RETURN
( Note: These routines all use canvas-space coordinates )
@get_mask_and_address ( x* y* -- mask row_address* )
OVR* OVR* ;get_sprite_address CALL ( x* y* sprite_addr* )
SWP* MOD8* ADD* ( x* row_addr* )
STH* ;get_mask CALL STHr* RETURN
@get_sprite_address ( x* y* -- sprite_address* )
SWP* DIV8* SWP* DIV8* ( spritex* spritey* )
/V.CANVAS.WIDTH? CEIL8DIV8* ( sprite_x* sprite_y* sprite_width* )
MUL* ADD* MUL8* ;canvas_buffer ADD* ( sprite_addr* )
RETURN
@get_mask ( x* -- mask )
NIP MOD8 #0107 ROT SUB SHL4 SFT RETURN
@fill_canvas_from_point ( colour x* y* )
( Store starting values )
STH* ROT ;&col_p STA ( x* | y* )
DUP* ;&paint_x STA* ( x* | y* )
STHr* DUP* ;&paint_y STA* ( x* y* )
( Return if coords not in bounds )
;check_in_canvas_bounds CALL ( in_bounds? )
IF_FALSE RETURN ( -- )
( Find target colour )
;&paint_x LDA* ;&paint_y LDA* ( x* y* )
;get_canvas_pixel_colour CALL ( target_colour )
DUP ;&col_t STA ( target_colour )
( Return if target and paint colours are the same )
;&col_p LDA IF_EQUAL RETURN ( -- )
( Clear variables )
#0000 OVRk ;&mark1_a STA ;&mark2_a STA
;&backtrack STA ;&findloop STA ;&paint_d STA
( Move the painter to a wall to start )
&move_into_place ( -- )
;&front? CALL NOT ,&START JCN
;&move_forward CALL ,&move_into_place JMP
&MAIN_LOOP
;&move_forward CALL
( if right-pixel is inside, then )
;&right? CALL NOT ,&START JCN
( if backtrack is true and findloop is false and either front-pixel or left-pixel is inside, then )
;&backtrack LDA ;&findloop LDA NOT AND ;&front? CALL ;&left? CALL ORA AND NOT ,&no_findloop JCN
#01 ;&findloop STA
&no_findloop
;&turn_right CALL
&PAINT
;&move_forward CALL
&START
;&update_neighbour_wall_count CALL
;&wall_count LDA #04 EQU ,&check_1 JCN
( Orient the painter to face right along a wall )
&orient_right
;&front? CALL NOT ,&orient_left JCN
;&turn_right CALL ,&orient_right JMP
&orient_left
;&front? CALL ,&check_1 JCN
;&turn_left CALL ,&orient_left JMP
&check_1
;&wall_count LDA #01 NEQ ,&check_2 JCN
( If backtrack is True, findloop = True )
&1_1 ;&backtrack LDA NOT ,&1_2 JCN
#01 ;&findloop STA ;&MAIN_LOOP GOTO
( Else if findloop is True, make mark1 active )
&1_2 ;&findloop LDA NOT ,&1_3 JCN
#01 ;&mark1_a STA ;&MAIN_LOOP GOTO
( Else if front-left and back-left are both inside )
&1_3 ;&front_left? CALL ;&back_left? CALL AND NOT ;&MAIN_LOOP JCN*
#00 ;&mark1_a STA
;&paint_here CALL
;&PAINT GOTO
&check_2
;&wall_count LDA #02 NEQ ;&check_3 JCN*
( If back is not inside )
&2_1 ;&back? CALL ,&2_2 JCN
( If front-left is inside )
;&front_left? CALL NOT ;&MAIN_LOOP JCN*
#00 ;&mark1_a STA
;&paint_here CALL
;&PAINT GOTO
( If mark1 is not active )
&2_2 ;&mark1_a LDA ,&2_3 JCN
;&paint_x LDA* ;&mark1_x STA*
;&paint_y LDA* ;&mark1_y STA*
;&paint_d LDA ;&mark1_d STA
#0001 ;&mark1_a STA ;&mark2_a STA
#0000 ;&backtrack STA ;&findloop STA
;&MAIN_LOOP GOTO
( Else if mark2 is not active )
&2_3 ;&mark2_a LDA ;&2_4 JCN*
( If painter is at mark1 )
&2_3_1 ;&mark1_a LDA NOT ,&2_3_2 JCN
;&paint_x LDA* ;&mark1_x LDA* NEQ* ,&2_3_2 JCN
;&paint_y LDA* ;&mark1_y LDA* NEQ* ,&2_3_2 JCN
( If painter and mark1 are facing in the same direction )
&2_3_1_1 ;&paint_d LDA ;&mark1_d LDA NEQ ,&2_3_1_else
#00 ;&mark1_a STA
;&turn_around CALL
;&paint_here CALL
;&PAINT GOTO
&2_3_1_else
#0001 ;&backtrack STA ;&findloop STA
;&mark1_d LDA ;&paint_d STA
;&MAIN_LOOP GOTO
( If findloop is True )
&2_3_2 ;&findloop LDA NOT ,&2_3_end JCN
;&paint_x LDA* ;&mark2_x STA*
;&paint_y LDA* ;&mark2_y STA*
;&paint_d LDA ;&mark2_d STA
#01 ;&mark2_a STA
&2_3_end ;&MAIN_LOOP GOTO
( Else if painter is at mark1 )
&2_4 ;&mark1_a LDA NOT ,&2_5 JCN
;&paint_x LDA* ;&mark1_x LDA* NEQ* ,&2_5 JCN
;&paint_y LDA* ;&mark1_y LDA* NEQ* ,&2_5 JCN
;&mark2_x LDA* ;&paint_x STA*
;&mark2_y LDA* ;&paint_y STA*
;&mark2_d LDA ;&paint_d STA
#0000 ;&mark1_a STA ;&mark2_a STA
#00 ;&backtrack STA
;&turn_around CALL
;&paint_here CALL
;&PAINT GOTO
( Else if painter is at mark2 )
&2_5 ;&mark2_a LDA NOT ,&2_end JCN
;&paint_x LDA* ;&mark2_x LDA* NEQ* ,&2_end JCN
;&paint_y LDA* ;&mark2_y LDA* NEQ* ,&2_end JCN
;&paint_x LDA* ;&mark1_x STA*
;&paint_y LDA* ;&mark1_y STA*
;&mark2_d LDA ;&mark1_d STA
;&mark2_d LDA ;&paint_d STA
#0001 ;&mark1_a STA ;&mark2_a STA
&2_end ;&MAIN_LOOP GOTO
&check_3
;&wall_count LDA #03 NEQ ,&check_4 JCN
#00 ;&mark1_a STA
;&paint_here CALL
;&PAINT GOTO
&check_4
;&wall_count LDA #04 NEQ ;&MAIN_LOOP JCN*
;&paint_here CALL_RET
( Update the variable tracking the number of walls around the painter )
&update_neighbour_wall_count ( -- wall_count )
#04 ,&front? JSR ,&back? JSR ,&left? JSR ,&right? JSR
ADD* ADD SUB ;&wall_count STA RETURN
( Return 1 if the pixel in the direction is inside the fill region, otherwise 0 )
&front? #00 ,&dir_inside? JMP
&back? #02 ,&dir_inside? JMP
&left? #03 ,&dir_inside? JMP
&right? #01 ,&dir_inside? JMP
&front_left? #03 ,&diag_inside? JMP
&back_left? #02 ,&diag_inside? JMP
&dir_inside? ;&col_t LDA SWP ,&get_dir_coords JSR ;check_canvas_pixel_colour CALL_RET
&diag_inside? ;&col_t LDA SWP ,&get_diag_coords JSR ;check_canvas_pixel_colour CALL_RET
( 0:front 1:right 2:back 3:left )
&get_dir_coords ( relative_d -- x* y* )
;&paint_d LDA ADD MOD4 ( d )
TO_SHORT DUP* INC MOD4 SWP* ( d+1* d* )
DOUBLE* ;&_dir_offsets ADD* LDA* SWP* ( d+1* | y_offset* )
DOUBLE* ;&_dir_offsets ADD* LDA* SWP* ( x_offset* | y_offset* )
,&to_abs_coords JMP
( 0:front-right 1:back-right 2:back-left 3:back-left )
&get_diag_coords ( relative_d -- x* y* )
;&paint_d LDA ADD MOD4 ( d )
#0003 ROT SUB INCk* MOD4 ( 3-d* 4-d* )
DOUBLE* ;&_diag_offsets ADD* LDA* SWP* ( y_offset* 3-d* )
DOUBLE* ;&_diag_offsets ADD* LDA* SWP* ( x_offset* y_offset* )
,&to_abs_coords JMP
&to_abs_coords ( x_offset* y_offset* -- x* y* )
SWP* ;&paint_x LDA* ADD* SWP* ( x* y_offset* )
;&paint_y LDA* ADD* RETURN ( x* y* )
&_dir_offsets ffff 0000 0001 0000
&_diag_offsets ffff ffff 0001 0001
&paint_here ,&col_p LDR ,&paint_x LDR* ,&paint_y LDR* ;set_canvas_pixel_colour CALL_RET
&move_forward #00 ;&get_dir_coords CALL ,&paint_y STR* ,&paint_x STR* RETURN
&turn_left #ff ,&_turn JMP
&turn_right #01 ,&_turn JMP
&turn_around #02 ,&_turn JMP
&_turn ,&paint_d LDR ADD MOD4 ,&paint_d STR RETURN
&paint_x $2 &mark1_x $2 &mark2_x $2 ( x-coords )
&paint_y $2 &mark1_y $2 &mark2_y $2 ( y-coords )
&paint_d $2 &mark1_d $1 &mark2_d $1 ( directions, 0:up 1:right 2:down 3:left )
&wall_count $1 &mark1_a $1 &mark2_a $1 ( neighbouring walls count, and mark active bools )
&col_p $1 &col_t $1 ( paint colour and target colour )
&backtrack $1 &findloop $1
( ---------------------------------------------------------------------------- )
( T O O L B O X )
@delay ( coarse* -- )
#0000 SWP* SUB* #0000 ( coarse* fine* )
&loop_fine
INC* DUP* ORA ,&loop_fine JCN
&loop_coarse
SWP* INC* SWP* OVR* ORA ,&loop_fine JCN
POP* POP* RETURN
@print_newline LIT <LINEFEED> /CONSOLE.WRITE! RETURN
@print_space LIT <SPACE> /CONSOLE.WRITE! RETURN
( Print a byte to the console in binary )
@print_byte_binary ( byte -- )
#80
&loop ( byte mask )
ANDk TO_BOOL LIT* "01 CHOOSE ( byte mask ascii )
/CONSOLE.WRITE! SHR1 DUP ,&loop JCN ( byte mask )
&end POP* RETURN
( Print a single byte to the console in decimal )
@print_byte_decimal_signed ( byte -- )
DUP* IS_POSITIVE ,print_byte_decimal JCN
LIT "- /CONSOLE.WRITE! NEGATE
@print_byte_decimal ( byte -- )
;convert_byte_to_decimal_string CALL
PRINTF(%s) RETURN
( Print a single short to the console in decimal )
@print_short_decimal_signed ( short* -- )
DUP* IS_POSITIVE* ,print_short_decimal JCN
LIT "- /CONSOLE.WRITE! NEGATE*
@print_short_decimal ( short* -- )
;convert_short_to_decimal_string CALL
PRINTF(%s) RETURN
( Print a null-terminated string to the console )
@print_string ( text_addr* -- )
&loop LDAk DUP ,&print JCN POP POP* RETURN
&print /CONSOLE.WRITE! INC* ,&loop JMP
( Print a value as TRUE or FALSE )
@print_bool_short ( bool* -- )
TO_BOOL*
@print_bool ( bool -- )
;&true ROT ,&print JCN POP* ;&false &print PRINTF(%s) RETURN
&true "TRUE <NULL> &false "FALSE <NULL>
( Print a region of memory to the console as shorts, excluding the end address )
@print_memory_region_shorts ( start* end* -- )
SWP*
&loop
EQUk* ,&end JCN
LDAk* PRINTF(%d*\s)
INC* INC* ,&loop JMP
&end POP* POP* PRINTF(\n) RETURN
( Convert an integer to a null-terminated string )
@convert_byte_to_decimal_string ( byte -- text_addr* )
TO_SHORT
@convert_short_to_decimal_string ( value* -- text_addr* )
;&array_end SWP*
&loop ( addr* value* )
#000a DIVMOD* ( addr* value/10* digit* )
DIGIT_TO_ASCII ( addr* value* junk ascii )
ROT* STAk NIP* DEC* SWP* ( addr-1* value* )
DUP* ADD ,&loop JCN ( addr* value* )
&end
POP* INC* RETURN ( text_addr* )
&array $4 &array_end $1 <NULL>
( ---------------------------------------------------------------------------- )
( U S E R I N T E R F A C E F R A M E W O R K )
( )
( A declarative callback-driven graphical user interface framework. )
( Enterprise-ready, functional, highly-available. )
( The center point of the screen, half-width and half-height )
%/I.CENTER.X { #e3 } %/I.CENTER.X? { #e3 LDZ* } %/I.CENTER.X! { #e3 STZ* }
%/I.CENTER.Y { #e5 } %/I.CENTER.Y? { #e5 LDZ* } %/I.CENTER.Y! { #e5 STZ* }
( The last-known position of the mouse cursor )
%/I.CURSOR.X { #e7 } %/I.CURSOR.X? { #e7 LDZ* } %/I.CURSOR.X! { #e7 STZ* }
%/I.CURSOR.Y { #e9 } %/I.CURSOR.Y? { #e9 LDZ* } %/I.CURSOR.Y! { #e9 STZ* }
( Edge states for mouse and controller )
%/I.BUTTON.PRESSED { #eb } %/I.BUTTON.PRESSED? { #eb LDZ } %/I.BUTTON.PRESSED! { #eb STZ }
%/I.BUTTON.HELD { #ec } %/I.BUTTON.HELD? { #ec LDZ } %/I.BUTTON.HELD! { #ec STZ }
%/I.BUTTON.RELEASED { #ed } %/I.BUTTON.RELEASED? { #ed LDZ } %/I.BUTTON.RELEASED! { #ed STZ }
%/I.MOUSE.PRESSED { #ee } %/I.MOUSE.PRESSED? { #ee LDZ } %/I.MOUSE.PRESSED! { #ee STZ }
%/I.MOUSE.HELD { #ef } %/I.MOUSE.HELD? { #ef LDZ } %/I.MOUSE.HELD! { #ef STZ }
%/I.MOUSE.RELEASED { #f0 } %/I.MOUSE.RELEASED? { #f0 LDZ } %/I.MOUSE.RELEASED! { #f0 STZ }
( The index of the currently-selected UI element )
%/I.ACTIVE_ELEMENT { #f1 } %/I.ACTIVE_ELEMENT? { #f1 LDZ } %/I.ACTIVE_ELEMENT! { #f1 STZ }
( The index of the previously-selected UI element )
%/I.PREV_ELEMENT { #f2 } %/I.PREV_ELEMENT? { #f2 LDZ } %/I.PREV_ELEMENT! { #f2 STZ }
( The total number of registered UI elements for this screen )
%/I.NUM_ELEMENTS { #f3 } %/I.NUM_ELEMENTS? { #f3 LDZ } %/I.NUM_ELEMENTS! { #f3 STZ }
( The orientation of the UI for this screen. 0: horizontal, 1: vertical )
%/I.ORIENTATION { #f4 } %/I.ORIENTATION? { #f4 LDZ } %/I.ORIENTATION! { #f4 STZ }
( The memory address of the current colour palette )
%/I.PALETTE_ADDR { #f5 } %/I.PALETTE_ADDR? { #f5 LDZ* } %/I.PALETTE_ADDR! { #f5 STZ* }
( The number of steps in the current colour palette )
%/I.PALETTE_SIZE { #f7 } %/I.PALETTE_SIZE? { #f7 LDZ } %/I.PALETTE_SIZE! { #f7 STZ }
( The position of the mouse cursor when mouse button 1 was last pressed )
%/I.MOUSE1.X { #f8 } %/I.MOUSE1.X? { #f8 LDZ* } %/I.MOUSE1.X! { #f8 STZ* }
%/I.MOUSE1.Y { #fa } %/I.MOUSE1.Y? { #fa LDZ* } %/I.MOUSE1.Y! { #fa STZ* }
( The position of the mouse cursor when mouse button 2 was last pressed )
%/I.MOUSE2.X { #fc } %/I.MOUSE2.X? { #fc LDZ* } %/I.MOUSE2.X! { #fc STZ* }
%/I.MOUSE2.Y { #fe } %/I.MOUSE2.Y? { #fe LDZ* } %/I.MOUSE2.Y! { #fe STZ* }
( Constants )
%HORIZONTAL { #00 } %VERTICAL { #01 }
( Actions )
%CENTER_LEFT? { /I.CENTER.X? SWP* SUB* } %CENTER_LEFT! { CENTER_LEFT? /SCREEN.X! }
%CENTER_RIGHT? { /I.CENTER.X? ADD* } %CENTER_RIGHT! { CENTER_RIGHT? /SCREEN.X! }
%CENTER_UP? { /I.CENTER.Y? SWP* SUB* } %CENTER_UP! { CENTER_UP? /SCREEN.Y! }
%CENTER_DOWN? { /I.CENTER.Y? ADD* } %CENTER_DOWN! { CENTER_DOWN? /SCREEN.Y! }
( Draw all UI elements )
@ui__draw_all ( -- )
/I.NUM_ELEMENTS? #00 ( count index )
&loop ( count index )
EQUk ,&end JCN ( count index )
DUP ;ui__draw_single CALL ( count index )
INC ,&loop JMP ( count index )
&end ( count index )
POP* RETURN ( -- )
( Draw a single UI element by index )
@ui__draw_single ( index -- )
DUP /I.ACTIVE_ELEMENT? EQU SWP ( active? index )
;ui__callbacks/draw ,__ui__call_callback JMP
( Call a callback for a specific UI element, [ index -- ] )
@ui__press_inc ;ui__callbacks/dec ,__ui__call_callback JMP
@ui__press_dec ;ui__callbacks/inc ,__ui__call_callback JMP
@ui__press_go ;ui__callbacks/go ,__ui__call_callback JMP
( Calculate and call the real address of a callback )
@__ui__call_callback ( index slot_addr* )
ROT DOUBLE TO_SHORT ( slot_addr* offset* )
ADD* LDA* ( real_callback_addr* )
CALL_RET ( -- )
( Register a separate callback for each user interface element.
Each of the following subroutines accepts a list of pairs of a control index
and a callback address, followed by the number of pairs that were provided.
For example: [ #00 ;func0 #01 ;func1 #04 ;func4 #03 ]
Draw callbacks MUST consume the 1-byte 'active?' bool that is passed to them.
Controller input callbacks (left, right, A) do not receive any data.
Callbacks are expected to return, and so SHOULD consume the return address
on the return stack by calling RETURN or NORETURN.
Signature for each 'register' subroutine is: [index callback*]+ count -- )
@ui__register_draw_callbacks ;ui__callbacks/draw ,__ui__register_callbacks JMP
@ui__register_dec_callbacks ;ui__callbacks/inc ,__ui__register_callbacks JMP
@ui__register_inc_callbacks ;ui__callbacks/dec ,__ui__register_callbacks JMP
@ui__register_go_callbacks ;ui__callbacks/go ,__ui__register_callbacks JMP
@__ui__register_callbacks ( [index callback*]+ count slot_addr* -- )
,&slot_addr STR* ( [index callback*]+ count )
&loop ( [index callback*]+ count )
STH ROT DOUBLE TO_SHORT ( ... callback* offset* | count )
[ LIT* &slot_addr $2 ] ADD* STA* ( ... | count )
STHr DEC DUP ,&loop JCN ( ... count )
POP RETURN ( -- )
( Register callbacks for raw button handling. [ addr* -- ] )
@ui__register_left_callback ;ui__callbacks/left STA* RETURN
@ui__register_right_callback ;ui__callbacks/right STA* RETURN
@ui__register_up_callback ;ui__callbacks/up STA* RETURN
@ui__register_down_callback ;ui__callbacks/down STA* RETURN
@ui__register_A_callback ;ui__callbacks/A STA* RETURN
@ui__register_B_callback ;ui__callbacks/B STA* RETURN
@ui__register_start_callback ;ui__callbacks/start STA* RETURN
@ui__register_select_callback ;ui__callbacks/select STA* RETURN
@ui__register_mouse1_callback ;ui__callbacks/mouse1 STA* RETURN
@ui__register_mouse2_callback ;ui__callbacks/mouse2 STA* RETURN
( Register a single screen rect for each of multiple UI elements )
@ui__register_zones ( [index left* top* width* height*]+ count -- )
( Note: We decrement right* and bottom* because the bounds of the zone are inclusive )
&loop STH STH* STH* STH* STH* ( ... [index] | count height* width* top* left* )
TO_SHORT MUL8* ;ui__zones ADD* ( ... zone_left_addr* | count height* width* top* left* )
STHkr* ROTr* ADDr* DECr* STHr* ( ... zone_left_addr* left* right* | count height* top* )
STHkr* ADDr* DECr* STH* STH* ( ... zone_left_addr* left* | count bottom* top* right* )
OVR* STA* ADD2* ( ... zone_right_addr* | count bottom* top* right* )
STHr* OVR* STA* ADD2* ( ... zone_top_addr* | count bottom* top* )
STHr* OVR* STA* ADD2* ( ... zone_bottom_addr* | count bottom* )
STHr* SWP* STA* STHr ( ... count )
DEC DUP ,&loop JCN
POP RETURN
( Call once the callbacks for all controls have been registered. This will
draw each control on-screen and will hi-jack all controller input in order
to automatically handle all control interaction. )
@ui__run_blank ( -- )
#0000 #00
@ui__run ( active_element total_elements orientation -- )
/I.ORIENTATION! /I.NUM_ELEMENTS! ( active )
DUP /I.ACTIVE_ELEMENT! /I.PREV_ELEMENT! ( -- )
;ui__draw_all CALL
;ui__on_screen /SCREEN.VECTOR!
;ui__on_mouse /MOUSE.VECTOR!
;ui__on_controller /CONTROLLER.VECTOR!
( Clear both of the stacks, in case some junk data has been left on them )
#00 /SYSTEM.WST! #00 /SYSTEM.RST! BRK ( -- )
( Clears all of the callbacks and zones that were defined by the previous
screen, and wipes the foreground layer of the screen device. Call this
subroutine before registering controls for a screen, to ensure that no
configurations are left over from the previous screen. )
@ui__clear
( Recalculate the screen center )
/SCREEN.WIDTH? HALVE* /I.CENTER.X!
/SCREEN.HEIGHT? HALVE* /I.CENTER.Y!
( Clear mouse button state, to prevent weird clicks when the new UI comes up )
#0000 #00 /I.MOUSE.PRESSED! /I.MOUSE.HELD! /I.MOUSE.RELEASED!
( Erase foreground layer of screen device )
COL_00 AND_FG ;sprite/blank ;fill_screen_with_sprite CALL
( Clear all registered controller callbacks )
;ui__callbacks ;ui__callbacks/end ;&null_callback
;ui__clear_memory_region CALL
( Clear all registered draw and mouse callbacks )
;ui__callbacks/draw ;ui__callbacks/end ;&pop_null_callback
;ui__clear_memory_region CALL
( Clear all registered mouse zones )
;ui__zones ;ui__zones/end #0000
;ui__clear_memory_region CALL
( Set mouse colour to 0 )
COL_T1 ;ui__set_cursor_colour CALL_RET
&pop_null_callback POP
&null_callback RETURN
@ui__set_cursor_colour ( sprite_colour -- )
;ui__redraw_cursor/cursor_colour STA RETURN
( Fill a region of memory with a given value. )
@ui__clear_memory_region ( start_addr* end_addr* value* -- )
STH* SWP* ( end* addr* | val* )
&loop ( end* addr* | val* )
EQUk* ,&end JCN ( end* addr* | val* )
STHkr* OVR* STA* ( addr* )
INC* INC* ,&loop JMP ( end* addr* | val* )
&end POP* POP* POPr* RETURN
( Mouse vector )
@ui__on_mouse ( -- )
;update_mouse_states CALL
,&mouse1_pressed JSR
,&mouse2_pressed JSR
MOUSE_MASK_1 ,&mask STR ;ui__callbacks/mouse1 ,&callback STR*
,&mouse_held JSR
,&mouse_released JSR
MOUSE_MASK_2 ,&mask STR ;ui__callbacks/mouse2 ,&callback STR*
,&mouse_held JSR
,&mouse_released JSR
,&cursor_moved JSR
;ui__update_cursor_position CALL BRK
&mask $1 &callback $2
&mouse1_pressed
/I.MOUSE.PRESSED? MOUSE_MASK_1 IF_NOT_MASK RETURN
;ui__press_go ;ui__call_on_hovered_element CALL
;ui__redraw_controls CALL
;ui__restart_mouse1_drag CALL_RET
&mouse2_pressed
/I.MOUSE.PRESSED? MOUSE_MASK_2 IF_NOT_MASK RETURN
;ui__restart_mouse2_drag CALL_RET
&mouse_held
/I.MOUSE.HELD? [ ,&mask LDR ] IF_NOT_MASK RETURN
,&did_cursor_move JSR IF_FALSE RETURN
#00 [ ,&callback LDR* ] LDA* CALL_RET
&mouse_released
/I.MOUSE.RELEASED? [ ,&mask LDR ] IF_NOT_MASK RETURN
#01 [ ,&callback LDR* ] LDA* CALL_RET
&cursor_moved
,&did_cursor_move JSR IF_FALSE RETURN
( Only draw the mouse cursor if at least one UI element has been registered )
;ui__redraw_cursor CALL
/I.NUM_ELEMENTS? IF_FALSEY RETURN
;ui__set_active_control ;ui__call_on_hovered_element CALL_RET
&did_cursor_move ( -- moved? )
/I.CURSOR.X? /MOUSE.X? NEQ* ( diff_x? )
/I.CURSOR.Y? /MOUSE.Y? NEQ* ( diff_x? diff_y? )
ORA RETURN ( moved? )
@ui__redraw_cursor
;sprite/cursor /SCREEN.ADDR!
( Erase the mouse cursor from the previous mouse position )
/I.CURSOR.X? /SCREEN.X! /I.CURSOR.Y? /SCREEN.Y!
COL_00 AND_FG /SCREEN.SPRITE!
( Draw the mouse cursor under the current mouse position )
/MOUSE.X? /SCREEN.X! /MOUSE.Y? /SCREEN.Y!
[ LIT &cursor_colour $1 ] AND_FG /SCREEN.SPRITE!
RETURN
@ui__restart_mouse1_drag ( -- )
/MOUSE.X? /I.MOUSE1.X! /MOUSE.Y? /I.MOUSE1.Y! RETURN
@ui__restart_mouse2_drag ( -- )
/MOUSE.X? /I.MOUSE2.X! /MOUSE.Y? /I.MOUSE2.Y! RETURN
@ui__update_cursor_position
/MOUSE.X? /I.CURSOR.X! /MOUSE.Y? /I.CURSOR.Y! RETURN
( The main controller logic, this handles all interactions with the declared
user interface )
@ui__on_controller
;update_button_states CALL
/I.BUTTON.PRESSED? IF_FALSEY BRK
( mask on_button horz. vert. )
BUTTON_MASK_UP ;ui__callbacks/up ;&inc ;&prev ,&call_if_pressed JSR
BUTTON_MASK_DOWN ;ui__callbacks/down ;&dec ;&next ,&call_if_pressed JSR
BUTTON_MASK_LEFT ;ui__callbacks/left ;&prev ;&dec ,&call_if_pressed JSR
BUTTON_MASK_RIGHT ;ui__callbacks/right ;&next ;&inc ,&call_if_pressed JSR
BUTTON_MASK_A ;ui__callbacks/A ;&go DUP* ,&call_if_pressed JSR
BUTTON_MASK_B ;ui__callbacks/B ;&null DUP* ,&call_if_pressed JSR
BUTTON_MASK_START ;ui__callbacks/start ;&null DUP* ,&call_if_pressed JSR
BUTTON_MASK_SELECT ;ui__callbacks/select ;&null DUP* ,&call_if_pressed JSR
;ui__redraw_controls CALL BRK
( Call callbacks if the button matching this mask was pressed.
Takes a normal callback and two orientation-dependant callbacks.
The normal callback is always called, but only the orientation-dependant
callback that matches the current UI orientation will be called. )
&call_if_pressed ( mask callback* horz_callback* vert_callback* -- )
STH* STH* /I.ORIENTATION? STHr* STHr* ( mask callback* ui_callback* )
CHOOSE* ,&ui_callback STR* ( mask callback* )
LDA* ,&callback STR* ( mask )
/I.BUTTON.PRESSED? IF_NOT_MASK RETURN ( -- )
LIT* &ui_callback $2 CALL ( -- )
LIT* &callback $2 CALL_RET ( -- )
&prev ( -- )
/I.ACTIVE_ELEMENT? IF_FALSEY RETURN
/I.ACTIVE_ELEMENT DEC_Z RETURN
&next ( -- )
/I.ACTIVE_ELEMENT? INC /I.NUM_ELEMENTS? IF_EQUAL RETURN
/I.ACTIVE_ELEMENT INC_Z RETURN
&dec /I.ACTIVE_ELEMENT? ;ui__press_dec CALL_RET
&inc /I.ACTIVE_ELEMENT? ;ui__press_inc CALL_RET
&go /I.ACTIVE_ELEMENT? ;ui__press_go CALL_RET
&null RETURN
( Redraw the active and previously-active controls, and set prev_active to active )
@ui__redraw_controls ( -- )
( Return if no controls have been registered )
/I.NUM_ELEMENTS? ,&cont JCN RETURN &cont
/I.ACTIVE_ELEMENT? /I.PREV_ELEMENT? ( active prev_active )
;ui__draw_single CALL ( active // draw prev_active input )
DUP /I.PREV_ELEMENT! ( active )
;ui__draw_single CALL
;ui__restart_palette CALL_RET
( Update the active control and redraw the screen )
@ui__set_active_control ( index -- )
DUP /I.ACTIVE_ELEMENT? NEQ ,&cont JCN POP RETURN
&cont /I.ACTIVE_ELEMENT! ;ui__redraw_controls CALL_RET
( If there is a control under the mouse cursor, push the index of that
control to the stack and call the given routine. The index must be
consumed by the routine. )
@ui__call_on_hovered_element ( func_addr* -- )
#00
&loop ( f_addr* i )
DUP TO_SHORT MUL8* ( f_addr* i ix8* )
;ui__zones ADD* ( f_addr* i zone_left_addr* )
( Test horizontal bounds )
/I.CURSOR.X? OVR* LDA* LTH* STH ( f_addr* i zone_left_addr* | lth_left? )
#0002 ADD* ( f_addr* i zone_right_addr* | lth_left? )
/I.CURSOR.X? OVR* LDA* GTH* STHr ( f_addr* i zone_right_addr* gth_right? lth_left? )
ADD ,&no_match JCN #0002 ADD* ( f_addr* i zone_top_addr* )
( Test vertical bounds )
/I.CURSOR.Y? OVR* LDA* LTH* STH ( f_addr* i zone_top_addr* | lth_top? )
#0002 ADD* ( f_addr* i zone_bot_addr* )
/I.CURSOR.Y? OVR* LDA* GTH* STHr ( f_addr* i zone_top_addr* gth_bot? lth_top? )
ADD ,&no_match JCN POP* ,&match JMP ( // varies )
&no_match ( f_addr* i junk_addr* )
POP* INC DUP /I.NUM_ELEMENTS? ( f_addr* i i count )
LTH ,&loop JCN ( f_addr* i )
POP POP* RETURN ( -- )
&match ( f_addr* i )
ROT ROT GOTO ( i )
@ui__on_screen ( -- )
( Check if a palette has been loaded by seeing if palette_size is greater than 0 )
/I.PALETTE_SIZE? ,&cycle_palette JCN
;ui__initialise_colour_palette CALL BRK
&cycle_palette
;&counter LDAk* INC* DUP* ROT* STA* ( counter* // increment the frame counter )
#0009 DIV* #00 /I.PALETTE_SIZE? ( counter/9* palette_size* )
MOD* #0006 MUL* ( addr_offset* )
/I.PALETTE_ADDR? ADD* ( palette_addr* )
;load_colour_palette CALL ( -- )
BRK
&counter $2
( Use a new colour palette for the UI. 'size' must be 1 or greater. )
@ui__set_colour_palette ( palette_addr* size )
/I.PALETTE_SIZE! /I.PALETTE_ADDR! RETURN
( Attempt to load the colour palette from the .theme file, otherwise fall
back to a default palette. )
@ui__initialise_colour_palette
( TODO: Implement .theme file loading )
;&default_palette /I.PALETTE_ADDR!
#01 /I.PALETTE_SIZE! RETURN
&default_palette 1d9b 1613 1011
( Resets the frame counter used for pulsing the screen colours )
@ui__restart_palette
#0000 ;ui__on_screen/counter STA* RETURN
( Callbacks are two-byte addresses )
@ui__callbacks
( One callback per UI element, signature [ -- ] )
&dec $32
&inc $32
&go $32
( One callback per controller button, signature [ -- ] )
&left $2
&right $2
&up $2
&down $2
&A $2
&B $2
&start $2
&select $2
( One callback per UI element, signature [ a -- ] )
&draw $32
( One callback per mouse action, signature [ state -- ]. 0: held, 1: released )
&mouse1 $2
&mouse2 $2
&end
( Zones are groups of left* right* top* bottom* inclusive bounds for each control )
@ui__zones
$128
&end
( Update the global button-state variables on the zero page )
@update_button_states ( -- )
/CONTROLLER.BUTTON? DUP /I.BUTTON.HELD? DUPk* ( held now prev now prev now prev )
EOR AND SWP SWP* EOR AND ( held released pressed )
/I.BUTTON.PRESSED! /I.BUTTON.RELEASED! /I.BUTTON.HELD! RETURN
( Update the global mouse-state variables on the zero page )
@update_mouse_states ( -- )
/MOUSE.STATE? DUP /I.MOUSE.HELD? DUPk* ( held now prev now prev now prev )
EOR AND SWP SWP* EOR AND ( held released pressed )
/I.MOUSE.PRESSED! /I.MOUSE.RELEASED! /I.MOUSE.HELD! RETURN
( ---------------------------------------------------------------------------- )
( P R O G R A M S C R E E N S )
( Show the initial "Create/Load Image" screen )
@launch_screen ( -- )
;ui__clear CALL
( Draw non-interactive elements )
;draw_menu_background CALL
#001c CENTER_LEFT!
#0018 CENTER_UP!
COL_01 ;&title ;draw_text CALL
( Register draw callbacks )
#00 ;launch_screen__draw__create_image
#01 ;launch_screen__draw__load_image
#02 ;ui__register_draw_callbacks CALL
( Register controller callbacks )
#00 ;launch_screen__go__create_image
#01 ;launch_screen__go__load_image
#02 ;ui__register_go_callbacks CALL
( Register mouse zones )
#00 #0032 CENTER_LEFT? #0008 CENTER_DOWN? #0064 #000b
#01 #0032 CENTER_LEFT? #0014 CENTER_DOWN? #0064 #000b
#02 ;ui__register_zones CALL
( Finish callback registration )
#00 #02 VERTICAL ;ui__run GOTO
&title "TUNGSTEN <NULL>
@launch_screen__draw__create_image ( active? )
#0032 CENTER_LEFT! #0008 CENTER_DOWN!
COL_21 COL_31 CHOOSE ( colour )
#0064 ;&label ;draw_button CALL_RET ( -- )
&label "Create <SPACE> "image <NULL>
@launch_screen__draw__load_image ( active? )
#0032 CENTER_LEFT! #0014 CENTER_DOWN!
COL_21 COL_31 CHOOSE ( colour )
#0064 ;&label ;draw_button CALL_RET ( -- )
&label "Load <SPACE> "image <NULL>
@launch_screen__go__create_image
NORETURN ;create_screen GOTO
@launch_screen__go__load_image
NORETURN ;load_screen GOTO
( Show the "Create Image" settings screen )
@create_screen ( -- )
;ui__clear CALL
( Draw non-interactive elements )
;draw_menu_background CALL
( Draw title )
#0023 CENTER_LEFT! #002a CENTER_UP!
COL_01 ;&title ;draw_text CALL
( Width/height labels )
#0032 CENTER_LEFT! #0006 CENTER_UP!
COL_01 ;&width_label ;draw_text CALL
#000c MOVE_DOWN
COL_01 ;&height_label ;draw_text CALL
( Register draw callbacks )
#00 ;create_screen__draw__back
#01 ;create_screen__draw__mode
#02 ;create_screen__draw__width
#03 ;create_screen__draw__height
#04 ;create_screen__draw__confirm
#05 ;ui__register_draw_callbacks CALL
( Register controller callbacks )
;launch_screen ;ui__register_B_callback CALL
#00 ;create_screen__go__back
#01 ;create_screen__go__mode
#04 ;create_screen__go__confirm
#03 ;ui__register_go_callbacks CALL
#01 ;create_screen__dec__mode
#02 ;create_screen__dec__width
#03 ;create_screen__dec__height
#03 ;ui__register_dec_callbacks CALL
#01 ;create_screen__inc__mode
#02 ;create_screen__inc__width
#03 ;create_screen__inc__height
#03 ;ui__register_inc_callbacks CALL
( Register mouse zones )
#00 #003c CENTER_LEFT? #002c CENTER_UP? #006a #000a
#01 #0032 CENTER_LEFT? #0018 CENTER_UP? #0064 #000d
#02 #0003 CENTER_RIGHT? #0008 CENTER_UP? #002f #000a
#03 #0003 CENTER_RIGHT? #0004 CENTER_DOWN? #002f #000a
#04 #0032 CENTER_LEFT? #001b CENTER_DOWN? #0064 #000a
#05 ;ui__register_zones CALL
( Finish callback registration )
#01 #05 VERTICAL ;ui__run GOTO
&title "CREATE <SPACE> "IMAGE <NULL>
&width_label "Width: <NULL>
&height_label "Height: <NULL>
@create_screen__draw__back ( active? )
#003c CENTER_LEFT! #002c CENTER_UP!
COL_21 COL_31 CHOOSE ( colour )
;sprite/back ;draw_icon_button CALL_RET ( -- )
@create_screen__draw__mode ( active? )
#0032 CENTER_LEFT! #0018 CENTER_UP!
COL_20 SWP COL_21 COL_31 CHOOSE ( bg fg )
#0064 ;&1_bit ;&2_bit /V.CANVAS.MODE? ( bg fg width* t1* t2* val )
;draw_toggle CALL_RET ( -- )
&1_bit "1-bit <NULL> &2_bit "2-bit <NULL>
@create_screen__draw__width ( active? )
#0003 CENTER_RIGHT! #0008 CENTER_UP!
STHk COL_21 COL_31 CHOOSE ( s_colour | active? )
#002f /V.CANVAS.WIDTH? ( s_colour width* width_val* | active? )
STHr ;draw_numeric_input CALL_RET ( s_colour width* width_val* active? )
@create_screen__draw__height ( active? )
#0003 CENTER_RIGHT! #0004 CENTER_DOWN!
STHk COL_21 COL_31 CHOOSE ( s_colour | active? )
#002f /V.CANVAS.HEIGHT? ( s_colour width* height_val* | active? )
STHr ;draw_numeric_input CALL_RET ( s_colour width* height_val* active? )
@create_screen__draw__confirm
#0032 CENTER_LEFT! #001b CENTER_DOWN!
COL_12 COL_13 CHOOSE
#0064 ;&confirm ;draw_button CALL_RET
&confirm "CONFIRM <NULL>
@create_screen__go__back
NORETURN ;launch_screen CALL_RET
@create_screen__go__mode
/V.CANVAS.MODE? NOT /V.CANVAS.MODE! RETURN
@create_screen__go__confirm
;recenter_canvas CALL
NORETURN ;canvas_screen GOTO
@create_screen__dec__mode
#00 /V.CANVAS.MODE! RETURN
@create_screen__dec__width
/V.CANVAS.WIDTH? #0008 SUB* /V.CANVAS.WIDTH! RETURN
@create_screen__dec__height
/V.CANVAS.HEIGHT? #0008 SUB* /V.CANVAS.HEIGHT! RETURN
@create_screen__inc__mode
#01 /V.CANVAS.MODE! RETURN
@create_screen__inc__width
/V.CANVAS.WIDTH? #0008 ADD* /V.CANVAS.WIDTH! RETURN
@create_screen__inc__height
/V.CANVAS.HEIGHT? #0008 ADD* /V.CANVAS.HEIGHT! RETURN
( Show the "Load Image" settings screen )
@load_screen
;ui__clear CALL
( Draw non-interactive elements )
;draw_menu_background CALL
#001d CENTER_LEFT! #002a CENTER_UP!
COL_01 ;&title ;draw_text CALL
( Register draw callbacks )
#00 ;load_screen__draw__back
#01 ;load_screen__draw__path
#02 ;load_screen__draw__load
#03 ;ui__register_draw_callbacks CALL
( Register controller callbacks )
;launch_screen ;ui__register_B_callback CALL
#00 ;load_screen__go__back
#02 ;load_screen__go__load
#02 ;ui__register_go_callbacks CALL
( Register mouse zones )
#00 #003c CENTER_LEFT? #002c CENTER_UP? #000a DUP*
#01 ;ui__register_zones CALL
( Finish callback registration )
#01 #03 VERTICAL ;ui__run GOTO
&title "LOAD <SPACE> "IMAGE <NULL>
@load_screen__draw__back
#003c CENTER_LEFT! #002c CENTER_UP!
COL_21 COL_31 CHOOSE
;sprite/back ;draw_icon_button CALL_RET
@load_screen__draw__path ( active? -- )
#003b CENTER_LEFT! #0000 CENTER_DOWN!
COL_21 COL_31 CHOOSE
#0074 ;&path ;draw_button CALL_RET
&path "/uxn/untitled.tng <NULL>
@load_screen__draw__load
#0032 CENTER_LEFT! #001b CENTER_DOWN!
COL_12 COL_13 CHOOSE
#0064 ;&label ;draw_button CALL_RET
&label "LOAD <NULL>
@load_screen__go__back
;launch_screen GOTO
@load_screen__go__load
;recenter_canvas CALL
NORETURN ;canvas_screen GOTO
@canvas_screen
;ui__clear CALL
;colour_palette_canvas #04 ;ui__set_colour_palette CALL
#03 ;ui__set_cursor_colour CALL
;canvas_screen__draw CALL
;canvas_screen__tool ;ui__register_mouse1_callback CALL
;canvas_screen__B ;ui__register_B_callback CALL
;ui__run_blank GOTO
BRK
@canvas_screen__draw COL_2 ;fill_screen CALL ;draw_canvas CALL_RET
@canvas_screen__B NORETURN ;tool_palette_screen GOTO
@canvas_screen__tool
( Dispatch control to the routine of the active tool )
/V.ACTIVE_TOOL? MUL4 JMP
;&pan GOTO ;&zoom GOTO ;&brush_1 GOTO ;&brush_2 GOTO
;&brush_3 GOTO ;&line GOTO ;&rect GOTO ;&fill GOTO
&pan ,&pan_released JCN ( released? -- )
&pan_held
( Erase old indicator rectangle )
/I.CURSOR.X? /I.MOUSE1.X? SUB* /V.CANVAS.X? ADD* /SCREEN.X!
/I.CURSOR.Y? /I.MOUSE1.Y? SUB* /V.CANVAS.Y? ADD* /SCREEN.Y!
COL_0 AND_FG /V.CANVAS.WIDTH? /V.CANVAS.HEIGHT? ;draw_rect_outline CALL
( Draw new indicator rectangle )
/MOUSE.X? /I.MOUSE1.X? SUB* /V.CANVAS.X? ADD* /SCREEN.X!
/MOUSE.Y? /I.MOUSE1.Y? SUB* /V.CANVAS.Y? ADD* /SCREEN.Y!
COL_3 AND_FG /V.CANVAS.WIDTH? /V.CANVAS.HEIGHT? ;draw_rect_outline CALL_RET
&pan_released
/MOUSE.X? /I.MOUSE1.X? SUB* /V.CANVAS.X? ADD* /V.CANVAS.X!
/MOUSE.Y? /I.MOUSE1.Y? SUB* /V.CANVAS.Y? ADD* /V.CANVAS.Y!
COL_0 ;fill_foreground CALL
;canvas_screen__draw CALL_RET
&zoom RETURN
&brush_1
POP
/MOUSE.X? /V.CANVAS.X? SUB* DUP* ,&brush_1_x STR*
/V.CANVAS.WIDTH? LTH* IF_FALSE RETURN
/MOUSE.Y? /V.CANVAS.Y? SUB* DUP* ,&brush_1_y STR*
/V.CANVAS.HEIGHT? LTH* IF_FALSE RETURN
[ LIT* &brush_1_x $2 LIT* &brush_1_y $2 ]
;get_mask_and_address CALL ( mask addr* )
( Modify the canvas buffer )
LDAk STH ROT STHr ORA ROT ROT STA
( Draw a dot to the screen without needing to redraw the entire buffer )
/MOUSE.X? /SCREEN.X! /MOUSE.Y? /SCREEN.Y! COL_1 /SCREEN.PIXEL!
RETURN
&brush_2 RETURN
&brush_3 RETURN
&line RETURN
&rect RETURN
&fill ( released? -- )
IF_FALSE RETURN #01
/MOUSE.X? /V.CANVAS.X? SUB*
/MOUSE.Y? /V.CANVAS.Y? SUB*
;fill_canvas_from_point CALL
;draw_canvas CALL
RETURN
( Render the canvas to the screen from the raw program data )
@draw_canvas ( -- )
COL_2 ;fill_screen CALL ( -- )
#05 /SCREEN.AUTO! ( -- )
/V.CANVAS.Y? /SCREEN.Y! ( -- )
;canvas_buffer /SCREEN.ADDR! ( -- )
/V.CANVAS.HEIGHT? CEIL8DIV8* ( sprite_height* )
&new_line ( sprite_height* )
/V.CANVAS.X? /SCREEN.X! ( sprite_height* )
/V.CANVAS.WIDTH? CEIL8DIV8* ( sprite_height* sprite_width* )
&loop ( sprite_height* sprite_width* )
COL_01 /SCREEN.SPRITE! ( sprite_height* sprite_width* )
DEC* DUP* GTH0* ,&loop JCN ( sprite_height* sprite_width* )
&move_down ( sprite_height* sprite_width* )
POP* DEC* #0008 MOVE_DOWN ( sprite_height* )
DUP* ADD ,&new_line JCN ( sprite_height* )
&end ( 0* )
/SCREEN.AUTO! POP RETURN ( -- )
@tool_palette_screen
;ui__clear CALL
;colour_palette_ui #04 ;ui__set_colour_palette CALL
( Draw non-interactive elements )
COL_0 ;fill_screen CALL
;draw_tool_palette_background CALL
( Register draw callbacks )
#00 ;tool_palette_screen__draw_tool_1
#01 ;tool_palette_screen__draw_tool_2
#02 ;tool_palette_screen__draw_tool_3
#03 ;tool_palette_screen__draw_tool_4
#04 ;tool_palette_screen__draw_tool_5
#05 ;tool_palette_screen__draw_tool_6
#06 ;tool_palette_screen__draw_tool_7
#07 ;tool_palette_screen__draw_tool_8
#08 ;ui__register_draw_callbacks CALL
( Register controller callbacks )
#00 ;canvas_screen
#01 ;canvas_screen
#02 ;canvas_screen
#03 ;canvas_screen
#04 ;canvas_screen
#05 ;canvas_screen
#06 ;canvas_screen
#07 ;canvas_screen
#08 ;ui__register_go_callbacks CALL
;canvas_screen
;ui__register_B_callback CALL
( Register mouse zones )
#00 ,tool_palette_screen__get_mouse_zone JSR
#01 ,tool_palette_screen__get_mouse_zone JSR
#02 ,tool_palette_screen__get_mouse_zone JSR
#03 ,tool_palette_screen__get_mouse_zone JSR
#04 ,tool_palette_screen__get_mouse_zone JSR
#05 ,tool_palette_screen__get_mouse_zone JSR
#06 ,tool_palette_screen__get_mouse_zone JSR
#07 ,tool_palette_screen__get_mouse_zone JSR
#08 ;ui__register_zones CALL
( Finish callback registration )
/V.ACTIVE_TOOL? #08 HORIZONTAL ;ui__run GOTO
@tool_palette_screen__get_mouse_zone ( index -- index left* top* width* height* )
LITr 00 STHk /SCREEN.WIDTH? ( index s_width* | index* )
;get_tool_palette_width CALL ( index s_width* width* | index* )
SUB* HALVE* ADD2* ( index zone_0_x* | index* )
STHr* #001a MUL* ADD* ( index left* )
#0018 CENTER_UP? #0017 DUP* RETURN ( index left* top* width* height* )
@tool_palette_screen__draw_tool_1 ( active? )
#00 ,tool_palette_screen__draw_tool JMP ( -- )
@tool_palette_screen__draw_tool_2 ( active? )
#01 ,tool_palette_screen__draw_tool JMP ( -- )
@tool_palette_screen__draw_tool_3 ( active? )
#02 ,tool_palette_screen__draw_tool JMP ( -- )
@tool_palette_screen__draw_tool_4 ( active? )
#03 ,tool_palette_screen__draw_tool JMP ( -- )
@tool_palette_screen__draw_tool_5 ( active? )
#04 ,tool_palette_screen__draw_tool JMP ( -- )
@tool_palette_screen__draw_tool_6 ( active? )
#05 ,tool_palette_screen__draw_tool JMP ( -- )
@tool_palette_screen__draw_tool_7 ( active? )
#06 ,tool_palette_screen__draw_tool JMP ( -- )
@tool_palette_screen__draw_tool_8 ( active? )
#07 ,tool_palette_screen__draw_tool JMP ( -- )
@tool_palette_screen__draw_tool ( active? index -- )
SWP /I.ACTIVE_ELEMENT? /V.ACTIVE_TOOL! ( index active? )
;draw_tool_palette_icon CALL_RET ( -- )
@draw_tool_palette_icon ( index active? )
LITr 00 STHk* POP LITr 00 STH ( | index* active index* )
#001a CENTER_UP! /SCREEN.WIDTH? ( s_width* | index* active index* )
;get_tool_palette_width CALL ( s_width* width* | index* active index* )
SUB* HALVE* ADD2* ( zone_0_x* | index* active index* )
STHr* #001a MUL* ADD* /SCREEN.X! ( | index* active )
STHr STHkr /V.ACTIVE_TOOL? EQU ( active? current_tool? | index* )
COL_0 COL_2 CHOOSE COL_3 CHOOSE ( colour | index* )
#0017 DUP* ;draw_capsule_smooth CALL ( | index* )
COL_T1 ;tool_icon STHr* #0048 MUL* ADD* ( colour 3x3_sprite_addr* )
;draw_3x3_sprite CALL_RET
( Draw a large capsule to use as a background for the tool icons )
@draw_tool_palette_background ( -- )
#001c CENTER_UP! COL_1 ( -- )
;get_tool_palette_width CALL ( colour width* )
/SCREEN.WIDTH? OVR* SUB* HALVE* ( colour width* bg_x* )
/SCREEN.X! #001b ;draw_capsule CALL_RET ( -- )
@get_tool_palette_width ( -- width* )
#0008 #001a MUL* INC* RETURN ( width* )
( Draw a 1-bit 3x3 sprite )
@draw_3x3_sprite ( colour sprite_addr* -- )
/SCREEN.ADDR! #26 /SCREEN.AUTO!
/SCREEN.SPRITE DEOk DEOk DEO
#00 /SCREEN.AUTO! #0018 MOVE_UP
RETURN
@recenter_canvas ( -- )
/SCREEN.WIDTH? /V.CANVAS.WIDTH? SUB* HALVE* /V.CANVAS.X!
/SCREEN.HEIGHT? /V.CANVAS.HEIGHT? SUB* HALVE* /V.CANVAS.Y!
RETURN
( Draws the standard dialog box background used in the menu screens.
0x86 by 0x64, or 0d134 by 0d100 )
@draw_menu_background ( -- )
COL_0 ;fill_screen CALL
( Draw awkward shadow corner )
#0040 CENTER_RIGHT! #002f CENTER_DOWN!
COL_2 #0004 DUP* ;draw_capsule CALL
( Draw inner frame, straight lines )
#0040 CENTER_LEFT! #0032 CENTER_UP!
COL_1 #0080 ;draw_horizontal_line CALL
#0041 CENTER_LEFT! #0031 CENTER_DOWN!
COL_1 #0081 ;draw_horizontal_line CALL
#0043 CENTER_LEFT! #002f CENTER_UP!
COL_1 #005e ;draw_vertical_line CALL
#0042 CENTER_RIGHT! #0030 CENTER_UP!
COL_1 #005f ;draw_vertical_line CALL
( Draw inner corners )
;&corner /SCREEN.ADDR!
#0042 CENTER_LEFT! #0031 CENTER_UP!
COL_T1 /SCREEN.SPRITE!
#003a CENTER_RIGHT! ( #0031 CENTER_UP! )
COL_T1 AND_FLIPX /SCREEN.SPRITE!
#0042 CENTER_LEFT! #0029 CENTER_DOWN!
COL_T1 AND_FLIPY /SCREEN.SPRITE!
#003a CENTER_RIGHT! ( #0029 CENTER_DOWN! )
COL_T1 AND_FLIPX AND_FLIPY /SCREEN.SPRITE!
( Draw drop shadow )
#0040 CENTER_LEFT! #0032 CENTER_DOWN!
COL_2 #0084 ;draw_horizontal_line CALL
#003e CENTER_LEFT! #0033 CENTER_DOWN!
COL_2 #0080 ;draw_horizontal_line CALL
#0043 CENTER_RIGHT! #002f CENTER_UP!
COL_2 #0061 ;draw_vertical_line CALL
#0044 CENTER_RIGHT! #002d CENTER_UP!
COL_2 #005e ;draw_vertical_line CALL_RET
&corner c080 0000 0000 0000
( Draws the standard dialog box background used in the menu screens )
@draw_menu_background_old ( -- )
COL_0 ;fill_screen CALL
#0041 CENTER_LEFT! #0031 CENTER_UP!
COL_1 #0082 #0062 ;draw_rect_outline CALL
#0043 CENTER_LEFT! #0033 CENTER_UP!
COL_2 #0086 #0066 ;draw_rect_outline CALL
RETURN
( ---------------------------------------------------------------------------- )
( U S E R I N T E R F A C E E L E M E N T S )
( Draw a variable-width 11-high button with a centered text label )
@draw_button ( sprite_colour width* text_addr* -- )
/SCREEN.X? STH* STH* STH* ( s_colour | startx* t_addr* width* )
DUP TO_PIXEL_COLOUR ( s_colour p_colour | startx* t_addr* width* )
STHkr* #000b ;draw_capsule CALL ( s_colour | startx* t_addr* width* )
( Draw text in center of button )
/SCREEN.Y? INC* INC* /SCREEN.Y! ( s_colour | startx* t_addr* width* )
SWPr* STHr* STHr* ( s_colour t_addr* width* | startx* )
OVR* ;get_text_width CALL SUB* HALVE* ( s_colour t_addr* t_x_offset* | startx* )
STHkr* ADD* /SCREEN.X! ;draw_text CALL ( s_colour t_addr* | startx* )
( Return pointer to initial position )
STHr* /SCREEN.X! ( -- )
/SCREEN.Y? #0002 SUB* /SCREEN.Y! ( -- )
RETURN ( -- )
( Draw a 10x10 square button containing an 8x8 2-bit icon sprite )
@draw_icon_button ( s_colour sprite_addr* -- )
/SCREEN.ADDR! DUP TO_PIXEL_COLOUR ( s_colour p_colour )
#000a DUP* ;draw_capsule CALL ( s_colour )
r/SCREEN.X? INCkr* r/SCREEN.X! ( s_colour | startx* )
r/SCREEN.Y? INCkr* r/SCREEN.Y! ( s_colour | startx* starty* )
/SCREEN.SPRITE! ( | startx* starty* )
r/SCREEN.Y! r/SCREEN.X! RETURN ( -- )
( Draw a two-option selector, 13-high. )
@draw_toggle ( bg_colour fg_colour width* text_addr_1* text_addr_2* value -- )
( Store calculated values )
r/SCREEN.Y? r/SCREEN.X? STH ( ... | y* x* val )
,&text_addr_2 STR* ,&text_addr_1 STR* ( bg fg width* | y* x* val )
DUP* #0004 SUB* HALVE* ,&half_width STR* ( bg fg width* | y* x* val )
DUP* #0001 AND* ,&pad STR* ,&width STR* ( bg fg | y* x* val )
( Convert [<edge,inner> <active,text>] to [<edge,inner> <active,text> <inner,text>] )
SWPk #03 AND SHL2 SWP #03 AND ORA ( bg active inactive | y* x* val )
STHr JMP SWP ( bg right left | y* x* )
,&left_colour STR ,&right_colour STR ( bg | y* x* )
( Draw edge )
DUP SHR2 [ LIT* &width $2 ] ( bg edge width* | y* x* )
#000d ;draw_capsule CALL ( bg | y* x* )
( Draw inner fill )
MOVE_RIGHT_1 MOVE_DOWN_1 ( bg | y* x* )
#03 AND ,&width LDR* #0002 SUB* ( inner width* | y* x* )
#000b ;draw_capsule CALL ( | y* x* )
( Draw left option )
[ LIT &left_colour $1 ] [ LIT* &half_width $2 ]
[ LIT* &text_addr_1 $2 ] ;draw_button CALL
,&half_width LDR* ADD2* [ LIT* &pad $2 ] ADD* MOVE_RIGHT
( Draw right option )
[ LIT &right_colour $1 ] ,&half_width LDR*
[ LIT* &text_addr_2 $2 ] ;draw_button CALL
r/SCREEN.X! r/SCREEN.Y!
RETURN
( Draw a numeric input widget )
@draw_numeric_input ( colour width* number* active -- )
/SCREEN.X? ,&start_x STR*
STH STH* STH* ( colour | active number* width* )
DUP ,&colour STR STHr* ( colour width* | active number* )
DUP* ,&width STR* STHr* ( colour width* number* | active )
;convert_short_to_decimal_string CALL ( colour width* text_addr* | active )
;draw_button CALL ( -- | active )
STHr JMP RETURN ( // return if inactive )
;sprite/left_chevron /SCREEN.ADDR!
#0002 MOVE_DOWN #0002 MOVE_RIGHT ( // position cursor for left-arrow )
,&colour LDR /SCREEN.SPRITE! ( // draw left arrow )
,&width LDR* #000c SUB* MOVE_RIGHT ( // position cursor for right-arrow )
,&colour LDR AND_FLIPX /SCREEN.SPRITE!
[ LIT* &start_x $2 ] /SCREEN.X!
#0002 MOVE_UP
RETURN
&colour $1 &width $2
( ---------------------------------------------------------------------------- )
( D R A W I N G S U B R O U T I N E S )
@set_colours ( red* green* blue* -- )
/SYSTEM.BLUE! /SYSTEM.GREEN! /SYSTEM.RED!
RETURN
( Load a colour palette stored in program memory )
@load_colour_palette ( colour_palette_addr* -- )
LDAk* SWP* INC* INC*
LDAk* SWP* INC* INC*
LDA* ;set_colours GOTO
@fill_foreground ( pixel_colour -- )
TO_SPRITE_COLOUR AND_FG ;sprite/blank
;fill_screen_with_sprite GOTO
( Fill the screen with one solid colour )
@fill_screen ( pixel_colour -- )
TO_SPRITE_COLOUR ;sprite/blank ( s_colour )
@fill_screen_with_sprite ( s_colour sprite_addr* -- )
/SCREEN.ADDR! ( s_colour )
#0000 DUP* /SCREEN.X! /SCREEN.Y! ( s_colour )
#f1 /SCREEN.AUTO! ( s_colour )
&loop ( s_colour )
DUP /SCREEN.SPRITE! ( s_colour )
/SCREEN.X? /SCREEN.WIDTH? LTH* ( s_colour is_line_incomplete )
,&loop JCN ( colour )
&next_line ( colour )
#0000 /SCREEN.X! ( colour )
/SCREEN.Y? #0008 ADD* ( colour y* )
DUP* /SCREEN.Y! ( colour y* )
/SCREEN.HEIGHT? LTH* ( colour is_screen_incomplete )
,&loop JCN
#00 /SCREEN.AUTO! POP RETURN
( Draw a rectange outline. Width and height must not be zero. )
@draw_rect_outline ( colour width* height* )
/SCREEN.X? ,&startx STR*
/SCREEN.Y? ,&starty STR*
STH* STH* DUP #0001 SWP* STHkr* ( 1* colour* width* | height* width* )
;draw_horizontal_line CALL ( 1* colour | height* width* )
DUP OVR* OVRr* STHr* ,&reset JSR ( 1* colour* | height* width* )
STHkr* ;draw_horizontal_line CALL ( 1* colour | height* width* )
DUP OVR* STHr* SWP* ,&reset JSR ( 1* colour* | height* )
STHkr* ;draw_vertical_line CALL ( 1* colour | height* )
DUP OVR* DUP* ,&reset JSR ( 1* colour* | height* )
POP STHr* ;draw_vertical_line CALL ( -- )
DUP* ,&reset JMP
&reset
LIT* &starty $2 ADD* DEC* /SCREEN.Y!
LIT* &startx $2 ADD* DEC* /SCREEN.X!
RETURN
( Draw a solid rectange. Width and height must not be zero. )
@draw_rect_filled ( colour width* height* )
STH* ,&width STR* ,&colour STR STHr* ( height* )
/SCREEN.Y? DUP* ROT* ADD* SWP* ( target* starty* )
r/SCREEN.Y DEIkr* ROTr ( target* starty* | startx* port )
&loop ( target* y* | startx* port )
[ LIT &colour $1 LIT* &width $2 ] ( target* y* colour width* | startx* port )
;draw_horizontal_line CALL ( target* y* | startx* port )
INC* DUP* /SCREEN.Y! ( target* y+1* | startx* port )
NEQk* ,&loop JCN ( target* y* | startx* port )
&end ( target* y* | startx* port )
POP* POP* DEOr* RETURN ( -- )
( Draw a horizontal or vertical line. Line length must not be zero. )
@draw_vertical_line #2a02 INCk JMP ( colour height* -- )
@draw_horizontal_line #2801 ( colour width* -- )
@__draw_line ( colour len* axis auto )
/SCREEN.AUTO! STH DEIkr* ROTr ( colour len* axis | start* axis )
&loop ( colour len* | start* axis )
ROTk /SCREEN.PIXEL! POP* ( colour len* | start* axis )
DEC* DUP* #0000 NEQ* ,&loop JCN ( colour len-1* | start* axis )
&end ( colour len* | start* axis )
DEOr* /SCREEN.AUTO! POP* RETURN ( -- )
( Draw proportional text )
@draw_text ( colour text_addr* -- )
ROT ,&colour STR ( t_addr* )
r/SCREEN.X DEIkr* ROTr ( t_addr* | x* axis )
&loop ( t_addr* | x* axis )
LDAk DUP ,&render JCN ( t_addr* ascii | x* axis )
POP POP* DEOr* RETURN ( -- )
&render
ASCII_TO_CODE TO_SHORT DUP* MUL8* ( t_addr* code* code8* | x* axis )
;acorn_font/characters ADD* ( t_addr* code* sprite* | x* axis )
/SCREEN.ADDR! ( t_addr* code* | x* axis )
[ LIT &colour $1 ] /SCREEN.SPRITE! ( t_addr* code* | x* axis )
;acorn_font/width ADD* LDA TO_SHORT ( t_addr* char_width* | x* axis )
/SCREEN.X? ADD* /SCREEN.X! ( t_addr* | x* axis )
INC* ,&loop JMP ( t_addr* | x* axis )
@get_text_width ( text_addr* -- width* )
#0000 SWP* ( width* t_addr* )
&loop ( width* t_addr* )
LDAk DUP ,&cont JCN ( width* t_addr* ascii )
&end ( width* t_addr* ascii )
POP POP* RETURN ( width* )
&cont ( width* t_addr* ascii )
ASCII_TO_CODE TO_SHORT ( width* t_addr* code* )
;acorn_font/width ADD* LDA TO_SHORT ( width* t_addr* char_width* )
ROT* ADD* SWP* INC* ,&loop JMP ( width* t_addr* )
@draw_capsule ( colour width* height* )
r/SCREEN.X? r/SCREEN.Y? ( c w* h* | x* y* )
INCkr* r/SCREEN.Y! ( c w* h* | x* y* )
( Draw wider rectangle )
STH* STH* DUP STHkr* OVRr* STHr* ( c c w* h-2* | x* y* h* w* )
#0002 SUB* ;draw_rect_filled CALL ( c | x* y* h* w* )
( Draw taller rectangle )
STHr* #0002 SUB* STHr* ( c w-2* h* | x* y* )
SWPkr* INCr* r/SCREEN.X! r/SCREEN.Y! ( c w-2* h* | x* y* )
;draw_rect_outline CALL ( | x* y* )
( Clear stacks )
r/SCREEN.Y! r/SCREEN.X! RETURN ( -- )
@draw_capsule_smooth ( colour width* height* )
r/SCREEN.X? r/SCREEN.Y? SWPkr* ( c w* h* | sx* sy* sy* sx* )
INCr* r/SCREEN.X! INCr* r/SCREEN.Y! ( c w* h* | sx* sy* )
( Draw inner rectangle )
STH* STH* DUPk ( c c c | sx* sy* h* w* )
STHkr* #0002 SUB* OVRr* STHr* #0002 SUB* ( c c c w-2* h-2* | sx* sy* h* w* )
;draw_rect_filled CALL ( c c | sx* sy* h* w* )
( Draw wider rectangle )
#0001 INCk* MOVE_DOWN MOVE_LEFT
STHkr* OVRr* STHr* #0006 SUB* ( c c w* h-6* | sx* sy* h* w* )
;draw_rect_outline CALL ( c | sx* sy* h* w* )
( Draw taller rectangle )
#0003 DUP* MOVE_RIGHT MOVE_UP
STHr* #0006 SUB* STHr* ( c w-6* h* | sx* sy* )
;draw_rect_outline CALL ( | sx* sy* )
( Clear stacks )
r/SCREEN.Y! r/SCREEN.X! RETURN ( -- )
( ---------------------------------------------------------------------------- )
( D A T A )
( b&w )
@colour_palette_canvas
e17a e17a e17a
e179 e179 e179
e178 e178 e178
e179 e179 e179
( graphics calculator )
@colour_palette_gfx
0e26 1e8b 2946
0e24 1e89 2945
0e22 1e88 2944
0e24 1e89 2945
( tungsten )
@colour_palette_ui
1d9b 1613 1011
1d9a 1612 1011
1d99 1611 1011
1d9a 1612 1011
( ea0 for a nice yellow for col3 )
@acorn_font
&width
0405 0408 0807 0802 0505 0807 0405 0307
0705 0707 0707 0707 0707 0304 0605 0607
0707 0707 0706 0607 0705 0707 0608 0807
0707 0707 0707 0708 0707 0705 0705 0706
0407 0707 0707 0607 0705 0507 0508 0707
0708 0707 0607 0708 0707 0705 0205 0808
&characters
0000 0000 0000 0000 ( ) 60f0 f060 6000 6000 ( ! )
a0a0 0000 0000 0000 ( " ) 6c6c fe6c fe6c 6c00 ( # )
107c e07c 0e7c 1000 ( $ ) cccc 1830 60cc cc00 ( % )
70d8 d870 dacc 7600 ( & ) 8080 0000 0000 0000 ( ' )
3060 c0c0 c060 3000 ( o_paren ) c060 3030 3060 c000 ( c_paren )
6c38 fe38 6c00 0000 ( * ) 0030 30fc 3030 0000 ( + )
0000 0000 0060 60c0 ( , ) 0000 00f0 0000 0000 ( - )
0000 0000 00c0 c000 ( . ) 0c18 1830 3060 60c0 ( / )
78cc dcfc eccc 7800 ( 0 ) 60e0 6060 6060 f000 ( 1 )
78cc 0c38 60c4 fc00 ( 2 ) 78cc 0c38 0ccc 7800 ( 3 )
1838 78d8 fc18 1800 ( 4 ) fcc0 f81c 0ccc 7800 ( 5 )
3860 c0f8 cccc 7800 ( 6 ) fc0c 1830 6060 6000 ( 7 )
78cc cc78 cccc 7800 ( 8 ) 78cc cc7c 0c18 7000 ( 9 )
00c0 c000 00c0 c000 ( : ) 0060 6000 0060 60c0 ( ; )
1830 60c0 6030 1800 ( < ) 0000 f000 f000 0000 ( = )
c060 3018 3060 c000 ( > ) 78cc 1830 3000 3000 ( ? )
78cc dcd4 dcc0 7800 ( @ ) 78cc cccc fccc cc00 ( A )
f8cc ccf8 cccc f800 ( B ) 78cc c0c0 c0cc 7800 ( C )
f8cc cccc cccc f800 ( D ) f8c0 c0f0 c0c0 f800 ( E )
f8c0 c0f0 c0c0 c000 ( F ) 78cc c0dc cccc 7800 ( G )
cccc ccfc cccc cc00 ( H ) f060 6060 6060 f000 ( I )
7c18 1818 18d8 7000 ( J ) cccc d8f0 d8cc cc00 ( K )
c0c0 c0c0 c0c0 f800 ( L ) c6ee fed6 d6c6 c600 ( M )
c6e6 f6de cec6 c600 ( N ) 78cc cccc cccc 7800 ( O )
f8cc cccc f8c0 c000 ( P ) 78cc cccc d4d8 6c00 ( Q )
f8cc ccf8 cccc cc00 ( R ) 78cc c078 0ccc 7800 ( S )
fc30 3030 3030 3000 ( T ) cccc cccc cccc 7800 ( U )
cccc cccc cc78 3000 ( V ) c6c6 c6d6 d6fe 6c00 ( W )
cccc 7830 78cc cc00 ( X ) cccc cc78 3030 3000 ( Y )
fc1c 3830 70e0 fc00 ( Z ) f0c0 c0c0 c0c0 f000 ( [ )
c060 6030 3018 180c ( \ ) f030 3030 3030 f000 ( ] )
3071 cc84 0000 0000 ( ^ ) 0000 0000 0000 f800 ( _ )
c060 0000 0000 0000 ( ` ) 0000 780c 7ccc 7c00 ( a )
c0c0 f8cc cccc f800 ( b ) 0000 78cc c0cc 7800 ( c )
0c0c 7ccc cccc 7c00 ( d ) 0000 78cc fcc0 7800 ( e )
3860 60f8 6060 6000 ( f ) 0000 7ccc cc7c 0c78 ( g )
c0c0 f8cc cccc cc00 ( h ) 6000 e060 6060 f000 ( i )
3000 7030 3030 30e0 ( j ) c0c0 ccd8 f0d8 cc00 ( k )
e060 6060 6060 f000 ( l ) 0000 6cfe d6d6 c600 ( m )
0000 f8cc cccc cc00 ( n ) 0000 78cc cccc 7800 ( o )
0000 f8cc ccf8 c0c0 ( p ) 0000 7ccc cc7c 0c0e ( q )
0000 d8ec c0c0 c000 ( r ) 0000 7ce0 781c f800 ( s )
6060 f860 6060 3800 ( t ) 0000 cccc cccc 7800 ( u )
0000 cccc cc78 3000 ( v ) 0000 c6d6 d6fe 6c00 ( w )
0000 cc78 3078 cc00 ( x ) 0000 cccc cc7c 0c78 ( y )
0000 fc18 3060 fc00 ( z ) 3060 60c0 6060 3000 ( { )
8080 8080 8080 8000 ( | ) c060 6030 6060 c000 ( } )
0000 62d6 8c00 0000 ( ~ )
@sprite
&blank 0000 0000 0000 0000
&full 1111 1111 1111 1111
&left_chevron 1030 70f0 7030 1000
&cursor 80c0 e0f0 f8e0 1000
&back 1830 60ff ff60 3018
&test_quarter 8f0f 0f0f 0f0f 0f8f 0000 0000 ffff ffff
&test_checker 55ab 55ab 55ab 55ab 0000 0000 ffff ffff
&cursor_pan c381 2408 1024 81c3
@tool_icon
&pan
0000 0000 0000 0000 0000 0010 387c d610 0000 0000 0000 0000
0206 0c1f 0c06 0200 1010 10ff 1010 1010 80c0 60f0 60c0 8000
0000 0000 0000 0000 d67c 3810 0000 0000 0000 0000 0000 0000
&zoom
0000 0001 0204 0808 0000 00f0 0874 120a 0000 0000 0000 0000
0808 0804 0201 0000 0a02 0204 0af7 0301 0000 0000 0000 80c0
0000 0000 0000 0000 0000 0000 0000 0000 e050 2000 0000 0000
&brush_1
0000 0000 0000 0000 0000 0814 1c1c 1c7f 0000 0000 0000 0000
0000 0000 0100 0000 7f00 7dff fe00 0008 0000 0000 0000 0000
0000 0000 0000 0000 1808 081c 0000 0000 0000 0000 0000 0000
&brush_2
0000 0000 0000 0000 0000 0814 1c1c 1c7f 0000 0000 0000 0000
0000 0000 0100 0000 7f00 7dff fe00 0018 0000 0000 0000 0000
0000 0000 0000 0000 2408 103c 0000 0000 0000 0000 0000 0000
&brush_3
0000 0000 0000 0000 0000 0814 1c1c 1c7f 0000 0000 0000 0000
0000 0000 0100 0000 7f00 7dff fe00 0018 0000 0000 0000 0000
0000 0000 0000 0000 2408 2418 0000 0000 0000 0000 0000 0000
&line
0000 0000 0000 0000 0000 0000 0001 0001 0000 0040 40f0 4040
0000 0000 0000 0005 0204 0810 2040 8000 0000 0000 0000 0000
041f 0404 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000
&rect
0000 0008 083e 080b 0000 0000 0000 00ff 0000 0020 20f8 20a0
0302 0302 0302 030b 55aa 55aa 55aa 55ff 8080 8080 8080 80a0
083e 0808 0000 0000 0000 0000 0000 0000 20f8 2020 0000 0000
&fill
0000 0000 0000 0001 0000 0000 385f af53 0000 0000 0000 c0e0
0205 0a14 1008 0402 8100 0401 1224 4890 f0f0 b030 3030 2020
0100 0000 0000 0000 20c0 0000 0000 0000 2000 0000 0000 0000
&select
0000 0000 0d08 0008 0000 0000 5500 0000 0000 0000 6020 0020
0008 0008 0008 0008 0000 0000 0000 0000 0020 0020 0020 0020
0008 0d00 0000 0000 0000 5500 0000 0000 0020 6000 0000 0000
@canvas_buffer
(
This label needs to be at the end of the
program, to ensure that there is enough space
to work with the largest possible image.
)
ffa1 adad a1bf 81ff ff81 f5d1 f781 c1ff ffff 0000 ffff 0000