From 07a35758f4dfafa4019f0bac1695e797d2256184 Mon Sep 17 00:00:00 2001 From: Ben Bridle Date: Sat, 25 Sep 2021 17:19:36 +1200 Subject: [PATCH] First commit UI framework mostly complete. Tool palette, canvas rendering, and new image creation works. Pan tool implemented, and rudimentary pixel brush for brush 1. This project had a number of commits from when I started development 9 months ago, but they were so primitive that they weren't worth keeping around. --- .gitignore | 1 + tungsten.tal | 1590 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 1591 insertions(+) create mode 100644 .gitignore create mode 100644 tungsten.tal diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a527d4f --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +*.rom \ No newline at end of file diff --git a/tungsten.tal b/tungsten.tal new file mode 100644 index 0000000..44e8262 --- /dev/null +++ b/tungsten.tal @@ -0,0 +1,1590 @@ +( ---------------------------------------------------------------------------- ) +( 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 } + +% { 00 } +% { 20 } +% { 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) } + + + +( ---------------------------------------------------------------------------- ) +( P R O G R A M S T A R T ) + +|0100 +@program_start ( -- ) + ( 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 S T U F F ) + +@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 + +( ---------------------------------------------------------------------------- ) +( T O O L B O X ) + +@print_newline LIT /CONSOLE.WRITE! RETURN +@print_space LIT /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 ) +@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 ) +@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 byte as TRUE or FALSE ) +@print_bool ( bool -- ) + ;&true ROT ,&print JCN POP* ;&false &print PRINTF(%s) + &true "TRUE &false "FALSE + + +( 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 + + +( ---------------------------------------------------------------------------- ) +( 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_RET + &pop_null_callback POP + &null_callback 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! + COL_T1 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 +@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 "image +@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 "image +@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 "IMAGE + &width_label "Width: + &height_label "Height: +@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 &2_bit "2-bit +@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 +@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 "IMAGE +@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 +@load_screen__draw__load + #0032 CENTER_LEFT! #001b CENTER_DOWN! + COL_12 COL_13 CHOOSE + #0064 ;&label ;draw_button CALL_RET + &label "LOAD +@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 + ;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 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 [ ] to [ ] ) + 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. +)