├── DATA.F08 ├── EZ80 USER MANUAL.pdf ├── README.md ├── ascii_codes.pdf ├── bitmap.asm ├── cond_debug.asm ├── crystal.data ├── crystal.png ├── debug.asm ├── dir_list.asm ├── ez80 spec.pdf ├── font-terminus.asm ├── fontAPI.asm ├── getinfo.asm ├── getkeys.bin ├── getpixel.asm ├── gpio_interrupt_rotary_01.asm ├── gpio_mode6_interrupts.asm ├── guess_number.bin ├── guess_number_02.asm ├── hello.asm ├── ink.asm ├── interrupts_UART_1.asm ├── interrupts_timer.asm ├── joystick.asm ├── joystick_interrupts_mode_6a.asm ├── joystick_ports.png ├── key_matrix.pdf ├── keyboard_interrupt.asm ├── mouse.asm ├── myMacros.inc ├── plot.asm ├── random.asm ├── rotary_encoder_state_machine.asm ├── savedata.asm ├── scroll.asm ├── showkey.asm ├── slowdown.asm ├── sound.asm ├── sprite.asm ├── star.data ├── text.asm ├── textinput.asm ├── udg.asm ├── usemacros.asm └── viewport.asm /DATA.F08: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/richardturnnidge/lessons/3cb69781f37a25d3ff7da6e31cd59a7ee4dc19b5/DATA.F08 -------------------------------------------------------------------------------- /EZ80 USER MANUAL.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/richardturnnidge/lessons/3cb69781f37a25d3ff7da6e31cd59a7ee4dc19b5/EZ80 USER MANUAL.pdf -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | The source files here are to accompany my YouTube lesson videos. 2 | 3 | 00 Introduction 4 | 5 | 01 Getting started with z80 assembly 6 | 7 | 02 Sending VDP Display Commands 8 | 9 | 03 Getting Info from MOS - the keyboard 10 | 11 | 04 Including files & macros 12 | 13 | 05 User Defined Graphics 14 | 15 | 06 Displaying the registers 16 | 17 | 07 Plotting graphics 18 | 19 | 08 Getting a pixel colour from VDP 20 | 21 | 09 Random number creation 22 | 23 | 10 Registers 24 | 25 | 11 Printing Bitmaps 26 | 27 | 12 Text Entry 28 | 29 | 13 Slow Down! 30 | 31 | 14 Reading Joysticks 32 | 33 | 15 Graphic Viewport 34 | 35 | 16 Sprites 36 | 37 | 17 Using the Mouse 38 | 39 | 18 Scrolling the Screen 40 | 41 | 19 MOSlet applications 42 | 43 | 20 Saving data in files 44 | 45 | 21 Playing Sounds 46 | 47 | 22 Reading a Directory Listing 48 | 49 | 23 USB Debugging & Conditional Assembly 50 | 51 | 24 Font API 52 | 53 | 25 Rotary Encoder 54 | 55 | 26 Interrupts - Programmable Reload Timer 56 | 57 | 27 Interrupts - GPIO Pins 58 | 59 | 28 Interrupts - Serial UART 60 | 61 | https://www.youtube.com/@AgonBits 62 | 63 | If you find these video lessons useful, you could buy me a coffee: 64 | https://www.buymeacoffee.com/richardturnnidge 65 | 66 | -------------------------------------------------------------------------------- /ascii_codes.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/richardturnnidge/lessons/3cb69781f37a25d3ff7da6e31cd59a7ee4dc19b5/ascii_codes.pdf -------------------------------------------------------------------------------- /bitmap.asm: -------------------------------------------------------------------------------- 1 | ; extra MACRO files need to go here 2 | include "myMacros.inc" 3 | 4 | .assume adl=1 ; ez80 ADL memory mode 5 | .org $40000 ; load code here 6 | 7 | jp start_here ; jump to start of code 8 | 9 | .align 64 ; MOS header 10 | .db "MOS",0,1 11 | 12 | start_here: 13 | 14 | push af ; store all the registers 15 | push bc 16 | push de 17 | push ix 18 | push iy 19 | 20 | ; ------------------ 21 | ; This is our actual code 22 | 23 | ; prepare the screen 24 | 25 | SET_MODE 8 ; mode 8 is 640x480 pixels, 64 colours 26 | 27 | 28 | ; Sending a VDU byte stream 29 | 30 | ld hl, VDUdata ; address of string to use 31 | ld bc, endVDUdata - VDUdata ; length of string 32 | rst.lil $18 ; Call the MOS API to send data to VDP 33 | 34 | ld a, $08 ; code to send to MOS 35 | rst.lil $08 ; get IX pointer to System Variables 36 | 37 | WAIT_HERE: ; loop here until we hit ESC key 38 | ld a, (ix + $05) ; get ASCII code of key pressed 39 | cp 27 ; check if 27 (ascii code for ESC) 40 | jp z, EXIT_HERE ; if pressed, jump to exit 41 | 42 | jr WAIT_HERE 43 | 44 | ; ------------------ 45 | ; This is where we exit the program 46 | 47 | EXIT_HERE: 48 | 49 | CLS 50 | pop iy ; Pop all registers back from the stack 51 | pop ix 52 | pop de 53 | pop bc 54 | pop af 55 | ld hl,0 ; Load the MOS API return code (0) for no errors. 56 | ret ; Return to MOS 57 | 58 | ; ------------------ 59 | ; This is the data we send to VDP 60 | 61 | crystal: EQU 0 ; used for bitmap ID number 62 | star: EQU 1 ; used for bitmap ID number 63 | 64 | VDUdata: 65 | .db 23, 0, 192, 0 ; set to non-scaled graphics 66 | 67 | ; LOAD THE BITMAP FROM A FILE 68 | ; file must be 24bit colour plus 8 bit alpha, byte order: RGBA 69 | ; 16x16 pixels RGBA should be 1,024 bytes in size 70 | 71 | .db 23, 27, 0, crystal ; select bitmap 0 - crystal 72 | .db 23, 27, 1 ; load bitmap data... 73 | .dw 16, 16 ; of size 16x16, from file: 74 | incbin "crystal.data" 75 | 76 | .db 23, 27, 0, star ; select bitmap 1 - star 77 | .db 23, 27, 1 ; load bitmap data... 78 | .dw 16, 16 ; of size 16x16, from file: 79 | incbin "star.data" 80 | 81 | ; PRINT THE BITMAP ON THE SCREEN VDP v1.04 onwards 82 | .db 23, 27, 0, crystal ; select bitmap 0 - crystal 83 | .db 23, 27, 3 ; draw selected bitmap at... 84 | .dw 80, 50 ; X, Y on the screen 85 | 86 | ; PLOT THE BITMAP ON THE SCREEN VDP v2.1.0 onwards 87 | .db 23, 27, 0, star ; select bitmap 0 - crystal 88 | .db 25, $ED ; plot absolute selected bitmap at... 89 | .dw 150, 120 ; X, Y on the screen 90 | 91 | endVDUdata: 92 | 93 | ; ------------------ 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | -------------------------------------------------------------------------------- /cond_debug.asm: -------------------------------------------------------------------------------- 1 | .assume adl=1 ; ez80 ADL memory mode 2 | .org $40000 ; load code here 3 | 4 | jp start_here ; jump to start of code 5 | 6 | .align 64 ; MOS header 7 | .db "MOS",0,1 8 | 9 | 10 | DEBUGGING: EQU 1 11 | 12 | macro DEBUGMSG whichMsg 13 | if DEBUGGING ; only gets assembled if DEBUGGING is true, else assembles nothing 14 | push af 15 | push hl ; HL and A are used, so save for when we are done 16 | 17 | ld a, 2 18 | rst.lil $10 ; enable 'printer' 19 | ld a, 21 20 | rst.lil $10 ; disable screen 21 | 22 | ld hl, whichMsg 23 | call printString 24 | 25 | ld a, 6 26 | rst.lil $10 ; re-enable screen 27 | ld a, 3 28 | rst.lil $10 ; disable printer 29 | 30 | pop hl 31 | pop af 32 | endif 33 | endmacro 34 | 35 | start_here: 36 | 37 | push af ; store all the registers 38 | push bc 39 | push de 40 | push ix 41 | push iy 42 | 43 | ; ------------------ 44 | ; This is our main program, just going to print hello World 45 | 46 | DEBUGMSG msg_started 47 | 48 | 49 | ld hl, msg_hello ; address of string to use 50 | ld bc,0 ; length of string, or 0 if a delimiter is used 51 | ld a,0 ; A is the delimiter 52 | rst.lil $18 ; Call the MOS API to send data to VDP 53 | 54 | 55 | DEBUGMSG msg_completed 56 | 57 | ; ------------------ 58 | ; This is where we exit the program 59 | 60 | pop iy ; Pop all registers back from the stack 61 | pop ix 62 | pop de 63 | pop bc 64 | pop af 65 | ld hl,0 ; Load the MOS API return code (0) for no errors. 66 | ret ; Return to MOS 67 | 68 | ; ------------------ 69 | 70 | printString: ; print zero terminated string 71 | ld a,(hl) 72 | or a 73 | ret z 74 | RST.LIL 10h 75 | inc hl 76 | jr printString 77 | 78 | ; ------------------ 79 | 80 | msg_hello: .db "Hello Agon World !\r\n",0 81 | 82 | msg_started: .db "We have started...\r\n",0 83 | msg_completed: .db "We completed the task...\r\n",0 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | -------------------------------------------------------------------------------- /crystal.data: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/richardturnnidge/lessons/3cb69781f37a25d3ff7da6e31cd59a7ee4dc19b5/crystal.data -------------------------------------------------------------------------------- /crystal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/richardturnnidge/lessons/3cb69781f37a25d3ff7da6e31cd59a7ee4dc19b5/crystal.png -------------------------------------------------------------------------------- /debug.asm: -------------------------------------------------------------------------------- 1 | ; --------------------------------------------- 2 | ; 3 | ; DEBUG ROUTINES - Richard Turnnidge 2023 4 | ; 5 | ; --------------------------------------------- 6 | 7 | printHexA: ; print A to screen as HEX byte pair at pos B,C 8 | 9 | ld (originalA), a ; store A for later 10 | 11 | ld a, 31 ; TAB at x,y 12 | rst.lil $10 13 | ld a, b ; x=B 14 | rst.lil $10 15 | ld a, c ; y=C 16 | rst.lil $10 ; put tab at BC position 17 | 18 | step1: 19 | ld a, (originalA) ; get A, then split into two nibbles 20 | and 11110000b ; get higher nibble 21 | rra 22 | rra 23 | rra 24 | rra ; move across to lower nibble 25 | add a,48 ; increase to ascii code range 0-9 26 | cp 58 ; is A less than 10? (58+) 27 | jr c, step2 ; carry on if less, else... 28 | add a, 7 ; add 7 to get A-F char if larger than 10 29 | 30 | step2: 31 | rst.lil $10 ; print the A char 32 | 33 | ld a, (originalA) ; get A back again 34 | and 00001111b ; now just get lower nibble 35 | add a,48 ; increase to ascii code range 0-9 36 | cp 58 ; is A less than 10 (58+) 37 | jp c, step3 ; carry on if less, else... 38 | add a, 7 ; add 7 to get A-F char if larger than 10 39 | 40 | step3: 41 | rst.lil $10 ; print the A char 42 | 43 | ld a, (originalA) ; get original A back 44 | ret ; return to main code 45 | 46 | originalA: .db 0 ; used to store A 47 | 48 | 49 | ; --------------------------------------------- 50 | 51 | printBin: ; take A as number and put into binary, B,C as X,Y 52 | 53 | push af ; store A for a moment 54 | 55 | ld a, 31 ; TAB at x,y 56 | rst.lil $10 57 | ld a, b ; x=b 58 | rst.lil $10 59 | ld a,c ; y=c 60 | rst.lil $10 ; put tab at BC position 61 | 62 | pop af ; get original A back again 63 | 64 | ld b, 8 ; number of bits to print 65 | ld hl, binString 66 | rpt: 67 | ld (hl), 48 ; ASCII 0 is 48, 1 is 49 ; reset first 68 | 69 | bit 7, a 70 | jr z, nxt 71 | ld (hl), 49 72 | nxt: 73 | inc hl ; next position in string 74 | rla 75 | djnz rpt 76 | 77 | ld hl, printStr 78 | ld bc, endPrintStr - printStr 79 | 80 | rst.lil $18 81 | 82 | ret 83 | 84 | 85 | printStr: ; data to send to VDP 86 | 87 | binString: .db "00000000" 88 | 89 | endPrintStr: 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | -------------------------------------------------------------------------------- /dir_list.asm: -------------------------------------------------------------------------------- 1 | .assume adl=1 ; ez80 ADL memory mode 2 | .org $40000 ; load code here 3 | 4 | jp start_here ; jump to start of code 5 | 6 | .align 64 ; MOS header 7 | .db "MOS",0,1 8 | 9 | macro MOSCALL afunc 10 | ld a, afunc 11 | rst.lil $08 12 | endmacro 13 | 14 | start_here: 15 | 16 | push af ; store all the registers 17 | push bc 18 | push de 19 | push ix 20 | push iy 21 | 22 | ; ------------------ 23 | 24 | 25 | ld hl, directoryPath ; where to store result 26 | ld bc, 255 ; max length 27 | MOSCALL $9E ; MOS api get current working directory 28 | 29 | ld hl, printDirHeading ; Sending initial text message 30 | call printString 31 | 32 | ld hl, directoryPath ; get pointer to the path 33 | ld bc, 0 34 | ld a, 0 ; it will be 0 terminated 35 | rst.lil $18 ; print result to screen 36 | 37 | ld hl, printCR ; address of string 38 | call printString ; print lf/cr 39 | 40 | 41 | ; now get dir info 42 | 43 | ld hl, DIR_struct ; define where to store directory info 44 | ld de, directoryPath ; this is pointer to the path to the directory 45 | MOSCALL $91 ; open dir 46 | 47 | 48 | _readFileInfo: ; we will loop here until all files have been processed 49 | 50 | ld hl, DIR_struct ; HL is where to get directory info 51 | ld de, FILINFO_struct ; define where to store current file info 52 | MOSCALL $93 ; read from dir 53 | 54 | ld a, (fname) ; get first char of file name 55 | cp 0 ; if 0 then we are at the end of the listing 56 | jr z, _allDone 57 | 58 | ld hl, fname ; this is pointer to the name of current file 59 | ld bc, 0 60 | ld a, 0 ; name will end with a 0 61 | rst.lil $18 ; print to screen 62 | 63 | ld hl, printCR ; now print a carriage retrun before the next entry 64 | call printString 65 | 66 | jr _readFileInfo ; loop around to check next entry 67 | 68 | _allDone: 69 | 70 | 71 | ld hl, DIR_struct ; load H: with address of the DIR struct 72 | MOSCALL $92 ; close dir 73 | 74 | 75 | ; ------------------ 76 | ; This is where we exit the program 77 | 78 | pop iy ; Pop all registers back from the stack 79 | pop ix 80 | pop de 81 | pop bc 82 | pop af 83 | ld hl,0 ; Load the MOS API return code (0) for no errors. 84 | ret ; Return to MOS 85 | 86 | ; ------------------ 87 | ; Some data stored here 88 | 89 | printDirHeading: 90 | .db "Our current directory is:\r\n",0 ; text to print 91 | 92 | printCR: 93 | .db "\r\n",0 ; text to print 94 | 95 | directoryPath: .BLKB 256,0 ; 256 x 0 bytes allocated for path name 96 | 97 | ; ------------------ 98 | ; Routine to print zero terminated string 99 | 100 | printString: 101 | ld a,(hl) 102 | or a 103 | ret z 104 | RST.LIL 10h 105 | inc hl 106 | jr printString 107 | 108 | ; ------------------ 109 | ; Structures used in the code above 110 | 111 | DIR_struct: 112 | dptr: .BLKB 4,0 ; Current read/write offset 113 | clust: .BLKB 4,0 ; Current cluster 114 | sect: .BLKB 4,0 ; Current sector (0:Read operation has terminated) 115 | dir: .BLKB 3,0 ; Pointer to the directory item in the win[] 116 | fn: .BLKB 12,0 ; SFN (in/out) {body[8],ext[3],status[1]} 117 | blk_ofs: .BLKB 4,0 ; Offset of current entry block being processed (0xFFFFFFFF:Invalid) 118 | end_DIR_struct: 119 | 120 | 121 | FILINFO_struct: 122 | fsize: .BLKB 4,0 ; File size 123 | fdate: .BLKB 2,0 ; Modified date 124 | ftime: .BLKB 2,0 ; Modified time 125 | fattrib: .BLKB 1,0 ; File attribute 126 | altname: .BLKB 13,0 ; Alternative file name 127 | fname: .BLKB 256,0 ; Primary file name 128 | end_FILINFO_struct: 129 | 130 | 131 | 132 | 133 | -------------------------------------------------------------------------------- /ez80 spec.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/richardturnnidge/lessons/3cb69781f37a25d3ff7da6e31cd59a7ee4dc19b5/ez80 spec.pdf -------------------------------------------------------------------------------- /fontAPI.asm: -------------------------------------------------------------------------------- 1 | 2 | .assume adl=1 ; ez80 ADL memory mode 3 | .org $40000 ; load code here 4 | 5 | jp start_here ; jump to start of code 6 | 7 | .align 64 ; MOS header 8 | .db "MOS",0,1 9 | 10 | start_here: 11 | 12 | push af ; store all the registers 13 | push bc 14 | push de 15 | push ix 16 | push iy 17 | 18 | ; ------------------ 19 | ; This is our actual code 20 | 21 | ; Setup fonts 22 | 23 | ld hl, loadData ; address of string to use 24 | ld bc, endData - loadData ; length of string 25 | rst.lil $18 ; Call the MOS API to send data to VDP 26 | 27 | 28 | ; print 1st message with font 1 29 | 30 | ld hl, msg1 ; address of string to use 31 | ld bc, endmsg1 - msg1 ; length of string 32 | rst.lil $18 ; Call the MOS API to send data to VDP 33 | 34 | ; print 2nd message with font 2 35 | 36 | ld hl, msg2 ; address of string to use 37 | ld bc, endmsg2 - msg2 ; length of string 38 | rst.lil $18 ; Call the MOS API to send data to VDP 39 | 40 | ; reset to system font 41 | 42 | ld hl, reset ; address of string to use 43 | ld bc, endReset - reset ; length of string 44 | rst.lil $18 ; Call the MOS API to send data to VDP 45 | 46 | 47 | ; ------------------ 48 | ; This is where we exit the program 49 | 50 | pop iy ; Pop all registers back from the stack 51 | pop ix 52 | pop de 53 | pop bc 54 | pop af 55 | ld hl,0 ; Load the MOS API return code (0) for no errors. 56 | ret ; Return to MOS 57 | 58 | ; ------------------ 59 | 60 | loadData: 61 | 62 | .db 23,0, $C0, 0 ; set display to non-scaled coordinates 63 | 64 | .db 12 ; CLS 65 | 66 | 67 | .db 23, 0, $A0 ; buffer command 68 | .dw -1 ; ID (word): -1 in this case is ALL 69 | .db 2 ; 2 = clear (all buffers) 70 | 71 | ; 1st font - load the buffer 72 | 73 | .db 23,0,$A0 ; buffer command 74 | .dw 1000 ; ID (word) 75 | .db 0 ; 'write' command 76 | .dw 2048 ; full 8x8 font is 16 bytes x 256 = 2048 77 | 78 | incbin "DATA.F08" 79 | 80 | 81 | ; create 1st font from buffer (VDU 23, 0, &95, 1, bufferId; width, height, ascent, flags) 82 | 83 | .db 23,0,$95 , 1 ; create font 84 | .dw 1000 ; ID (word) 85 | .db 8,8 ; 8x8 font in this example 86 | .db 8,0 ; ascent, flags 87 | 88 | 89 | 90 | ; 2nd font - load the buffer 91 | 92 | .db 23,0,$A0 ; buffer command 93 | .dw 1001 ; ID (word) 94 | .db 0 ; 'write' command 95 | .dw 4096 ; full 8x8 font is 16 bytes x 256 = 2048 96 | 97 | include "font-terminus.asm" 98 | 99 | ; create 2nd font from buffer(VDU 23, 0, &95, 1, bufferId; width, height, ascent, flags) 100 | 101 | .db 23,0,$95 , 1 ; create font 102 | .dw 1001 ; ID (word) 103 | .db 8 ,16 ; 8x8 font in this example 104 | .db 16,0 ; ascent, flags 105 | 106 | 107 | endData: 108 | 109 | ; ------------------ 110 | ; Messaes which get sent to VDP 111 | 112 | msg1: 113 | 114 | .db 4 ; print at TAB position 115 | 116 | .db 23,0,$95 , 0 ; select font (VDU 23, 0, &95, 0, bufferId; flags) 117 | .dw 1000 ; ID (word) 118 | .db 0 ; flags 119 | 120 | .db 31,4,20 ; TAB to 5, 5 121 | 122 | .db "8x8 font printed at TAB position" ; print this text 123 | .db 13,10 ; CR, LF 124 | 125 | endmsg1: 126 | 127 | ; ------------------ 128 | 129 | msg2: 130 | 131 | .db 5 ; print at PIXEL PLOT position 132 | 133 | .db 23,0,$95 , 0 ; select font (VDU 23, 0, &95, 0, bufferId; flags) 134 | .dw 1001 ; ID (word) 135 | .db 0 ; flags 136 | 137 | 138 | .db 18, 0, 1 ; set colour to red 139 | 140 | .db 25, $45 141 | .dw 20, 35 ; PLOT a pixel (red just so we can see it) 142 | 143 | .db 18, 0, 15 ; set colour to white 144 | 145 | .db "Custom ", 255, " 8x16_font at PIXEL position" ; print this text 146 | .db 13,10 ; CR, LF 147 | 148 | .db 4 ; print at TAB position 149 | 150 | endmsg2: 151 | 152 | ; ------------------ 153 | 154 | reset: 155 | 156 | .db 23,0,$95 , 0 ; select font (VDU 23, 0, &95, 0, bufferId; flags) 157 | .dw -1 ; ID (word) -1 is revert to system font 158 | .db 1 ; flags 159 | 160 | endReset: 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | -------------------------------------------------------------------------------- /getinfo.asm: -------------------------------------------------------------------------------- 1 | .assume adl=1 ; ez80 ADL memory mode 2 | .org $40000 ; load code here 3 | 4 | jp start_here ; jump to start of code 5 | 6 | .align 64 ; MOS header 7 | .db "MOS",0,1 8 | 9 | start_here: 10 | 11 | push af ; store all the registers 12 | push bc 13 | push de 14 | push ix 15 | push iy 16 | 17 | ; ------------------ 18 | ; This is our actual code 19 | 20 | ld hl, VDUdata ; address of string to use 21 | ld bc, endVDUdata - VDUdata ; length of string 22 | rst.lil $18 ; display message 23 | 24 | LOOP_HERE: ; loop here until we hit ESC or SPACE key 25 | 26 | ld a, $08 27 | rst.lil $08 ; get IX pointer to sysvars 28 | ld a, (ix + $05) 29 | cp 32 ; 32 is ascii code for SPACE 30 | jp z, EXIT_HERE ; if pressed, SPACE key to exit 31 | 32 | ld a, $1E 33 | rst.lil $08 ; get IX pointer to keyvals matrix of pressed keys 34 | ld a, (ix + $0E) 35 | bit 0, a ; index $0E, bit 0 is for ESC key in matrix 36 | jp nz, EXIT_HERE ; if pressed, ESC key to exit 37 | 38 | jr LOOP_HERE 39 | 40 | ; ------------------ 41 | ; This is where we exit the program 42 | 43 | EXIT_HERE: 44 | 45 | pop iy ; Pop all registers back from the stack 46 | pop ix 47 | pop de 48 | pop bc 49 | pop af 50 | ld hl,0 ; Load the MOS API return code (0) for no errors. 51 | ret ; Return to MOS 52 | 53 | ; ------------------ 54 | ; This is the data we send to VDP 55 | 56 | VDUdata: 57 | .db 22,4 ; set screen to MODE 8, this will also clear screen 58 | .db 31, 10, 10 ; TAB to 10,20 59 | .db "Press ESC to exit\r\n" ; print this text 60 | endVDUdata: 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /getkeys.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/richardturnnidge/lessons/3cb69781f37a25d3ff7da6e31cd59a7ee4dc19b5/getkeys.bin -------------------------------------------------------------------------------- /getpixel.asm: -------------------------------------------------------------------------------- 1 | ; extra MACRO files need to go here 2 | include "myMacros.inc" 3 | 4 | .assume adl=1 ; ez80 ADL memory mode 5 | .org $40000 ; load code here 6 | 7 | jp start_here ; jump to start of code 8 | 9 | .align 64 ; MOS header 10 | .db "MOS",0,1 11 | 12 | ; extra code files need to go here, or later 13 | include "debug.asm" 14 | 15 | start_here: 16 | 17 | push af ; store all the registers 18 | push bc 19 | push de 20 | push ix 21 | push iy 22 | 23 | ; ------------------ 24 | ; This is our actual code 25 | 26 | ; prepare the screen 27 | 28 | SET_MODE 8 ; mode 8 is 640x480 pixels, 64 colours 29 | 30 | 31 | ; Sending a VDU byte stream to create some graphics 32 | 33 | ld hl, VDUdata ; address of string to use 34 | ld bc, endVDUdata - VDUdata ; length of string 35 | rst.lil $18 ; Call the MOS API to send data to VDP 36 | 37 | 38 | MOSCALL $08 ; set IX pointer to the sysvars 39 | 40 | ; define which pixel to check ; NOTE pixel positions are WORDS 41 | ; so we need to define two bytes 42 | call resetReplyFlag ; make sure Flag is reset 43 | call doPixelQuery ; send the query to find pixel colour 44 | call waitForFlagUpdate ; wait for p flag to be updated 45 | 46 | ; NOTE (ix + 0A),(ix + 0B),(ix + 0C) contain R,B,G colour values 47 | ld a, (ix + $16) ; (ix + $16) contains the palette index of the colour 48 | 49 | ld b, 0 ; set B to X pos 50 | ld c, 0 ; set C to Y pos 51 | call printHexA ; print out what is in A at X, Y 52 | 53 | 54 | WAIT_HERE: ; loop here until we hit ESC key 55 | ld a, (ix + $05) ; get ASCII code of key pressed 56 | cp 27 ; check if 27 (ascii code for ESC) 57 | jp z, EXIT_HERE ; if pressed, jump to exit 58 | 59 | jr WAIT_HERE 60 | 61 | ; ------------------ 62 | ; This is where we exit the program 63 | 64 | EXIT_HERE: 65 | 66 | CLS 67 | pop iy ; Pop all registers back from the stack 68 | pop ix 69 | pop de 70 | pop bc 71 | pop af 72 | ld hl,0 ; Load the MOS API return code (0) for no errors. 73 | ret ; Return to MOS 74 | 75 | ; ------------------ 76 | ; This is the query we send to VDP 77 | 78 | doPixelQuery: ; routine sends request to VDP for pixel colour 79 | ld hl, vduQuery 80 | ld bc, endvduQuery - vduQuery 81 | rst.lil $18 82 | ret 83 | 84 | vduQuery: 85 | .db 23,0,$84 ; get pixel of colour at X, Y 86 | vduX: .dw 200 ; X is a word 87 | vduY: .dw 80 ; Y s a word 88 | endvduQuery: 89 | 90 | ; --------------------------------------------- 91 | 92 | resetReplyFlag: 93 | 94 | ld a, (ix + $04) ; get flag for point 95 | res 2,a ; reset the P flag 96 | ld (ix + $04), a ; store flag status 97 | ret 98 | 99 | waitForFlagUpdate: 100 | 101 | ld a, (ix + $04) ; plflags variable 102 | bit 2,a ; bit2 is result from a point query 103 | jr z, waitForFlagUpdate ; if not updated, then wait here 104 | 105 | ret 106 | 107 | ; --------------------------------------------- 108 | 109 | VDUdata: 110 | .db 23, 0, 192, 0 ; set to non-scaled graphics 111 | 112 | ; FOR A SINGLE PIXEL PLOT 113 | 114 | .db 18, 0, bright_red ; set graphics colour: mode (0), colour 115 | 116 | .db 25, 69 ; PLOT: mode (69 is a point in current colour), 117 | .dw 200,80 ; X; Y; 118 | 119 | ; FOR A LINE 120 | 121 | .db 18, 0, bright_magenta ; set graphics colour: mode (0), colour 122 | 123 | .db 25, 69 ; PLOT: mode (69 is a point in current colour), 124 | .dw 300, 60 ; X; Y; 125 | 126 | .db 25, 13 ; PLOT: mode (13 is a line), 127 | .dw 250,130 ; X; Y; 128 | 129 | ; FOR A RECTANGLE 130 | 131 | .db 18, 0, green ; set graphics colour: mode (0), colour 132 | 133 | .db 25, 69 ; PLOT: mode (69 is a point in current colour), 134 | .dw 10,120 ; X; Y; 135 | 136 | .db 25, 101 ; PLOT: mode (101 is a filled rectangle), 137 | .dw 100,180 ; X; Y; 138 | 139 | 140 | ; FOR A CIRCLE 141 | 142 | .db 18, 0, bright_yellow ; set graphics colour: mode (0), colour 143 | 144 | .db 25, 68 ; PLOT: mode (69 is a MOVE TO but don't plot point), 145 | .dw 180,140 ; X; Y; 146 | 147 | .db 25, 149 ; PLOT: mode (149 is an outlined circle), 148 | .dw 200,160 ; X; Y; 149 | 150 | ; FOR A FILLED TRIANGLE 151 | 152 | .db 18, 0, blue ; set graphics colour: mode (0), colour 153 | 154 | .db 25, 69 ; PLOT: mode (69 is a point in current colour), 155 | .dw 10,10 ; X; Y; 156 | 157 | .db 25, 69 ; PLOT: mode (69 is a point in current colour), 158 | .dw 50, 100 ; X; Y; 159 | 160 | .db 25, 85 ; PLOT: mode (85 is a filled triangle), 161 | .dw 200,20 ; X; Y; 162 | 163 | endVDUdata: 164 | 165 | ; ------------------ 166 | ; colour definitions - palette index colours 167 | 168 | bright_red: equ 9 169 | green: equ 2 170 | bright_yellow: equ 11 171 | bright_magenta: equ 13 172 | blue: equ 4 173 | white: equ 7 174 | black: equ 0 175 | bright_white: equ 15 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | -------------------------------------------------------------------------------- /gpio_interrupt_rotary_01.asm: -------------------------------------------------------------------------------- 1 | ; AGON LIGHT 2 | ; Interupt Driven Rotary Encoder 3 | ; Richard Turnnidge 2025 4 | ; State machine version 5 | 6 | ; --------------------------------------------- 7 | ; 8 | ; MACROS 9 | ; 10 | ; --------------------------------------------- 11 | 12 | macro MOSCALL function 13 | ld a, function 14 | rst.lil $08 15 | endmacro 16 | 17 | ; --------------------------------------------- 18 | 19 | macro TABTO x, y 20 | ld a, 31 21 | rst.lil $10 22 | ld a, x 23 | rst.lil $10 24 | ld a, y 25 | rst.lil $10 26 | endmacro 27 | 28 | ; --------------------------------------------- 29 | 30 | macro CLS 31 | ld a, 12 32 | rst.lil $10 33 | endmacro 34 | 35 | ; --------------------------------------------- 36 | 37 | macro SETCURSOR value ; to set cursor visible or not [0 or 1] 38 | push af 39 | ld a, 23 40 | rst.lil $10 41 | ld a, 1 42 | rst.lil $10 43 | ld a, value 44 | rst.lil $10 ; VDU 23,1,value [0 off, 1 on] 45 | pop af 46 | endmacro 47 | 48 | ; --------------------------------------------- 49 | ; 50 | ; CONSTANTS 51 | ; 52 | ; --------------------------------------------- 53 | 54 | ; GPIO port IDs 55 | 56 | PC_DR: equ $9E 57 | PC_DDR: equ $9F 58 | PC_ALT1: equ $A0 59 | PC_ALT2: equ $A1 60 | 61 | interruptPins: equ 10100000b 62 | 63 | CLK_pin: equ 10000000b 64 | DT_pin: equ 00100000b 65 | both_pins: equ 10100000b 66 | 67 | ; --------------------------------------------- 68 | ; 69 | ; INITIALISE AGON 70 | ; 71 | ; --------------------------------------------- 72 | 73 | .assume adl=1 ; big memory mode 74 | .org $40000 ; load code here 75 | 76 | jp start_here ; jump to start of code 77 | 78 | .align 64 ; MOS header 79 | .db "MOS",0,1 80 | 81 | include "debug_routines.asm" 82 | 83 | ; --------------------------------------------- 84 | ; 85 | ; INITIAL SETUP CODE HERE 86 | ; 87 | ; --------------------------------------------- 88 | 89 | start_here: 90 | 91 | push af ; store everything as good practice 92 | push bc ; pop back when we return from code later 93 | push de 94 | push ix 95 | push iy 96 | 97 | im 2 ; make sure running in interrupt mode 2 98 | 99 | CLS ; clear screen before we start 100 | SETCURSOR 0 ; hide the cursor 101 | 102 | TABTO 0,0 ; TAB to title position 103 | ld hl, msg_title ; put address of message into HL 104 | call printString ; print the demo title 105 | 106 | TABTO 0,2 ; TAB to title position 107 | ld hl, msg_title2 ; put address of message into HL 108 | call printString ; print the demo title 109 | 110 | call setup_GPIO_ports ; configure the GPIO pin settings 111 | 112 | call start_GPIO_interrupts ; start the interrupt listener 113 | 114 | ld a,0 115 | ld (state), a 116 | ld (encoder_value), a ; reset values 117 | 118 | ; --------------------------------------------- 119 | ; 120 | ; MAIN LOOP 121 | ; 122 | ; --------------------------------------------- 123 | 124 | MAIN_LOOP: 125 | 126 | get_key_input: 127 | MOSCALL $08 ; get IX pointer to sysvars 128 | ld a, (ix + 05h) ; ix+5h is 'last key pressed' 129 | 130 | cp 27 ; is it ESC key? 131 | jp z, exit_here ; if so exit cleanly 132 | 133 | 134 | 135 | jp MAIN_LOOP ; loop around until ESC is pressed 136 | 137 | 138 | ; --------------------------------------------- 139 | ; 140 | ; EXIT CODE CLEANLY 141 | ; 142 | ; --------------------------------------------- 143 | 144 | exit_here: 145 | 146 | call reset_GPIO ; reset GPIO port C to defaults 147 | 148 | CLS ; clear the screen 149 | SETCURSOR 1 ; show cursor 150 | 151 | pop iy 152 | pop ix 153 | pop de 154 | pop bc 155 | pop af 156 | ld hl,0 ; reset all values before returning to MOS 157 | 158 | ret ; return to MOS here 159 | 160 | ; --------------------------------------------- 161 | ; 162 | ; IO PORT INIT 163 | ; 164 | ; --------------------------------------------- 165 | ; 166 | ; This example uses GPIO Interrupt mode 6 - level-sensitive interrupts 167 | ; 168 | ; For each pin used, we set:- 169 | ; Data Register (DR) 1 170 | ; Data Direction Register (DDR) 0 171 | ; Alt1 register (ALT1) 0 172 | ; Alt2 register (ALT2) 1 173 | ; 174 | ; We are only working with interrupts on GPIO pins PC0 and PC1 in this example 175 | ; It defines pins 0 & 1 as interrupt driven input, and pins 2-7 as outputs 176 | 177 | 178 | setup_GPIO_ports: 179 | 180 | di ; stop any interrupts whle we configure 181 | 182 | ld a, interruptPins 183 | out0 (PC_DR), a ; set DR of PC5 and PC7 to 1 184 | 185 | ld a, 00000000b 186 | out0 (PC_DDR), a ; set DDR of register to 0 187 | 188 | ld a, 00000000b 189 | out0 (PC_ALT1), a ; set ALT1 of register to 0 190 | 191 | ld a, interruptPins 192 | out0 (PC_ALT2), a ; set ALT2 of PC5 and PC7 to 1 193 | 194 | ei ; re-enable the interrupts 195 | 196 | ret 197 | 198 | ; --------------------------------------------- 199 | 200 | start_GPIO_interrupts: 201 | 202 | ld hl, isr_gpio_PC5_event ; address of service routine to call 203 | ld e, $4A ; interrupt vector (GPIO Port PC0) 204 | MOSCALL $14 ; mos_api_setintvector 205 | 206 | ld hl, isr_gpio_PC7_event ; address of service routine to call 207 | ld e, $4E ; interrupt vector (GPIO Port PC1) 208 | MOSCALL $14 ; mos_api_setintvector 209 | 210 | ret 211 | 212 | ; --------------------------------------------- 213 | 214 | ; reset all of GPIO port C to default values 215 | ; ie. standard inputs 216 | 217 | reset_GPIO: 218 | 219 | di ; stop any interrupts whle we configure 220 | 221 | ld a, $00 222 | out0 (PC_DR), a ; set DR of all port C to 0 223 | 224 | ld a, $FF 225 | out0 (PC_DDR), a ; set DDR of all port C to 0 226 | 227 | ld a, $00 228 | out0 (PC_ALT1), a ; set ALT1 of all port C to 0 229 | 230 | ld a, $00 231 | out0 (PC_ALT2), a ; set ALT2 of all port C to 0 232 | 233 | ei ; re-enable the interrupts 234 | 235 | ret 236 | 237 | ; --------------------------------------------- 238 | ; 239 | ; INTERRUPT SERVICE ROUTINES 240 | ; 241 | ; GPIO interrupt service routine for pins PC 5 & 7 242 | ; Triggered when status changes from high to low, or vice versa 243 | ; 244 | ; --------------------------------------------- 245 | 246 | isr_gpio_PC5_event: 247 | 248 | di ; stop any other interrupts while we do this 249 | push hl 250 | push bc ; store any registers we are going to use 251 | push af ; store any registers we are going to use 252 | 253 | in0 a, (PC_DR) ; read the current status of the GPIO PC pins 254 | ld (lastPortC), a ; store latest port data 255 | 256 | or interruptPins ; only change relevant bits, leave others as they are 257 | out0 (PC_DR), a ; set DR of port C to acknowledge interrupt 258 | 259 | 260 | jp check_encoder 261 | 262 | ; --------------------------------------------- 263 | 264 | isr_gpio_PC7_event: 265 | 266 | di ; stop any other interrupts while we do this 267 | push hl 268 | push bc 269 | push af ; store any registers we are going to use 270 | 271 | in0 a, (PC_DR) ; read the current status of the GPIO PC pins 272 | 273 | ld (lastPortC), a ; store latest port data 274 | 275 | or interruptPins ; only change relevant bits0, leave others as they are 276 | out0 (PC_DR), a ; set DR of port C to acknowledge interrupt 277 | 278 | jp check_encoder 279 | 280 | ; --------------------------------------------- 281 | ; 282 | ; ROTARY STATE MACHINE 283 | ; 284 | ; Figure out if turning or we have completed a click 285 | ; 286 | ; --------------------------------------------- 287 | 288 | check_encoder: 289 | 290 | ld a, (lastPortC) 291 | 292 | and CLK_pin ; get CLK pin status 293 | ld (CLK_state), a ; store it 294 | 295 | ld a, (lastPortC) 296 | and DT_pin ; get DT pin status 297 | ld (DT_state), a ; store it 298 | 299 | or a ; clear flags to be sure 300 | 301 | ld a, (state) ; get current 'machine state' and jump to section 302 | cp 0 303 | jp z, state_0 304 | 305 | cp 1 306 | jp z, state_1 307 | 308 | cp 2 309 | jp z, state_2 310 | 311 | cp 3 312 | jp z, state_3 313 | 314 | cp 4 315 | jp z, state_4 316 | 317 | cp 5 318 | jp z, state_5 319 | 320 | cp 6 321 | jp z, state_6 322 | 323 | ; shouldn't get here, but just in case 324 | jp escapeInterrupt 325 | 326 | 327 | 328 | ; --------------------------------------------- 329 | ; state machine - we will be in one of these phases 330 | 331 | state_0: ; default start state 332 | 333 | ld a, (CLK_state) 334 | cp CLK_pin 335 | jr z, state_0_1 ; if CLK goes low 336 | 337 | ld a, 1 ; else set state to 1 338 | ld (state),a 339 | 340 | jp escapeInterrupt 341 | 342 | state_0_1: 343 | 344 | ld a, (DT_state) 345 | cp DT_pin 346 | jp z, escapeInterrupt ; if DT goes low 347 | 348 | ld a, 4 ; then set state to 4 349 | ld (state),a 350 | 351 | jp escapeInterrupt 352 | 353 | 354 | ; --------------------------------------------- 355 | state_1: 356 | 357 | ld a, (DT_state) ; CLK went low, now check DT 358 | cp DT_pin 359 | jp z, escapeInterrupt ; if DT goes high 360 | 361 | ld a, 2 ; set state to 2 if DT goes low 362 | ld (state),a 363 | 364 | jp escapeInterrupt 365 | 366 | ; --------------------------------------------- 367 | state_2: 368 | 369 | ld a, (CLK_state) ; check if CLK has now gone high 370 | cp CLK_pin 371 | jp nz, escapeInterrupt ; if CLK is low 372 | 373 | ld a, 3 ; set state to 3 if CLK is high 374 | ld (state),a 375 | 376 | jp escapeInterrupt 377 | 378 | 379 | ; --------------------------------------------- 380 | state_3: 381 | 382 | ld a, (lastPortC) 383 | cp both_pins ; are they both high? 384 | jp nz, escapeInterrupt ; not both 385 | 386 | ; completed one step in clockwise direction 387 | ld a, 0 388 | ld (state), a ; reset state 389 | 390 | ld a, (encoder_value) 391 | inc a 392 | ld (encoder_value), a ; increase value 393 | 394 | call encoderChanged ; notify of change 395 | 396 | jp escapeInterrupt 397 | 398 | ; --------------------------------------------- 399 | state_4: 400 | 401 | ld a, (CLK_state) ; DT went low, next check if CLK goes low 402 | cp CLK_pin 403 | jp z, escapeInterrupt ; if CLK is high 404 | 405 | ld a, 5 ; set state to 5 if CLK is low 406 | ld (state),a 407 | 408 | jp escapeInterrupt 409 | 410 | ; --------------------------------------------- 411 | state_5: 412 | 413 | ld a, (DT_state) ; CLK went low, next check if DT goes high 414 | cp DT_pin 415 | jp nz, escapeInterrupt ; if DT goes low 416 | 417 | ld a, 6 ; set state to 6 if DT goes high 418 | ld (state),a 419 | 420 | jp escapeInterrupt 421 | 422 | ; --------------------------------------------- 423 | state_6: 424 | 425 | ld a, (lastPortC) 426 | cp both_pins 427 | jp nz, escapeInterrupt ; not both 428 | 429 | ; completed one step in anticlockwise direction 430 | ld a, 0 431 | ld (state), a ; reset state 432 | 433 | ld a, (encoder_value) 434 | dec a 435 | ld (encoder_value), a ; decrease value 436 | 437 | call encoderChanged ; notify of change 438 | 439 | jp escapeInterrupt 440 | 441 | ; --------------------------------------------- 442 | ; finish all checks and end interrupt 443 | 444 | escapeInterrupt: 445 | 446 | pop af 447 | pop bc 448 | pop hl ; retrieve registers we have used 449 | 450 | ei ; re-enable interrupts for next time 451 | reti.l ; return from interrupt routine 452 | 453 | ; --------------------------------------------- 454 | ; this gets called if there is a chage to the encoder value 455 | 456 | encoderChanged: 457 | ; the encoder changed its value so we get alerted here 458 | ld b, 7 459 | ld c, 2 460 | ld a, (encoder_value) 461 | call debugA 462 | 463 | ret 464 | 465 | 466 | ; --------------------------------------------- 467 | ; 468 | ; text & data 469 | ; 470 | ; --------------------------------------------- 471 | 472 | msg_title: .asciz "Rotary Encoder Interrupt Handler " 473 | msg_title2: .asciz "Value: " 474 | 475 | state: .db 0 476 | pin_reading: .db 0 477 | CLK_state: .db 0 478 | DT_state: .db 0 479 | 480 | encoder_value: .db 0 481 | 482 | lastPortC: .db 0 483 | 484 | ; --------------------------------------------- 485 | ; 486 | ; END 487 | ; 488 | ; --------------------------------------------- 489 | -------------------------------------------------------------------------------- /gpio_mode6_interrupts.asm: -------------------------------------------------------------------------------- 1 | ; AGON GPIO INTERRUPTS 2 | ; Richard Turnnidge 2025 3 | ; Mode 6 example 4 | 5 | ; --------------------------------------------- 6 | ; 7 | ; MACROS 8 | ; 9 | ; --------------------------------------------- 10 | 11 | macro MOSCALL function 12 | ld a, function 13 | rst.lil $08 14 | endmacro 15 | 16 | ; --------------------------------------------- 17 | 18 | macro TABTO x, y 19 | ld a, 31 20 | rst.lil $10 21 | ld a, x 22 | rst.lil $10 23 | ld a, y 24 | rst.lil $10 25 | endmacro 26 | 27 | ; --------------------------------------------- 28 | 29 | macro CLS 30 | ld a, 12 31 | rst.lil $10 32 | endmacro 33 | 34 | ; --------------------------------------------- 35 | 36 | macro SETCURSOR value ; to set cursor visible or not [0 or 1] 37 | push af 38 | ld a, 23 39 | rst.lil $10 40 | ld a, 1 41 | rst.lil $10 42 | ld a, value 43 | rst.lil $10 ; VDU 23,1,value [0 off, 1 on] 44 | pop af 45 | endmacro 46 | 47 | ; --------------------------------------------- 48 | ; 49 | ; CONSTANTS 50 | ; 51 | ; --------------------------------------------- 52 | 53 | ; GPIO port IDs 54 | 55 | PC_DR: equ $9E 56 | PC_DDR: equ $9F 57 | PC_ALT1: equ $A0 58 | PC_ALT2: equ $A1 59 | 60 | ; --------------------------------------------- 61 | ; 62 | ; INITIALISE AGON 63 | ; 64 | ; --------------------------------------------- 65 | 66 | .assume adl=1 ; big memory mode 67 | .org $40000 ; load code here 68 | 69 | jp start_here ; jump to start of code 70 | 71 | .align 64 ; MOS header 72 | .db "MOS",0,1 73 | 74 | ; --------------------------------------------- 75 | ; 76 | ; INITIAL SETUP CODE HERE 77 | ; 78 | ; --------------------------------------------- 79 | 80 | start_here: 81 | 82 | push af ; store everything as good practice 83 | push bc ; pop back when we return from code later 84 | push de 85 | push ix 86 | push iy 87 | 88 | im 2 ; make sure running in interrupt mode 2 89 | 90 | CLS ; clear screen before we start 91 | SETCURSOR 0 ; hide the cursor 92 | 93 | TABTO 2,8 ; TAB to title position 94 | ld hl, msg_title ; put address of message into HL 95 | call printString ; print the demo title 96 | 97 | call setup_GPIO_ports ; configure the GPIO pin settings 98 | 99 | call start_GPIO_interrupts ; start the interrupt listener 100 | 101 | ; --------------------------------------------- 102 | ; 103 | ; MAIN LOOP 104 | ; 105 | ; --------------------------------------------- 106 | 107 | MAIN_LOOP: 108 | 109 | get_key_input: 110 | MOSCALL $08 ; get IX pointer to sysvars 111 | ld a, (ix + 05h) ; ix+5h is 'last key pressed' 112 | 113 | cp 27 ; is it ESC key? 114 | jp z, exit_here ; if so exit cleanly 115 | 116 | jp MAIN_LOOP ; loop around until ESC is pressed 117 | 118 | 119 | ; --------------------------------------------- 120 | ; 121 | ; EXIT CODE CLEANLY 122 | ; 123 | ; --------------------------------------------- 124 | 125 | exit_here: 126 | 127 | call reset_GPIO ; reset GPIO port C to defaults 128 | 129 | CLS ; clear the screen 130 | SETCURSOR 1 ; show cursor 131 | 132 | pop iy 133 | pop ix 134 | pop de 135 | pop bc 136 | pop af 137 | ld hl,0 ; reset all values before returning to MOS 138 | 139 | ret ; return to MOS here 140 | 141 | ; --------------------------------------------- 142 | ; 143 | ; IO PORT INIT 144 | ; 145 | ; --------------------------------------------- 146 | ; 147 | ; This example uses GPIO Interrupt mode 6 - level-sensitive interrupts 148 | ; 149 | ; For each pin used, we set:- 150 | ; Data Register (DR) 1 151 | ; Data Direction Register (DDR) 0 152 | ; Alt1 register (ALT1) 0 153 | ; Alt2 register (ALT2) 1 154 | ; 155 | ; We are only working with interrupts on GPIO pins PC0 and PC1 in this example 156 | ; It defines pins 0 & 1 as interrupt driven input, and pins 2-7 as outputs 157 | 158 | 159 | setup_GPIO_ports: 160 | 161 | di ; stop any interrupts whle we configure 162 | 163 | ld a, 00000011b 164 | out0 (PC_DR), a ; set DR of PC0 and PC1 to 1 165 | 166 | ld a, 00000000b 167 | out0 (PC_DDR), a ; set DDR of register to 0 168 | 169 | ld a, 00000000b 170 | out0 (PC_ALT1), a ; set ALT1 of register to 0 171 | 172 | ld a, 00000011b 173 | out0 (PC_ALT2), a ; set ALT2 of PC0 and PC1 to 1 174 | 175 | ei ; re-enable the interrupts 176 | 177 | ret 178 | 179 | ; --------------------------------------------- 180 | 181 | start_GPIO_interrupts: 182 | 183 | ld hl, isr_gpio_PC0_event ; address of service routine to call 184 | ld e, $40 ; interrupt vector (GPIO Port PC0) 185 | MOSCALL $14 ; mos_api_setintvector 186 | 187 | ld hl, isr_gpio_PC1_event ; address of service routine to call 188 | ld e, $42 ; interrupt vector (GPIO Port PC1) 189 | MOSCALL $14 ; mos_api_setintvector 190 | 191 | ret 192 | 193 | ; --------------------------------------------- 194 | 195 | ; reset all of GPIO port C to default values 196 | ; ie. standard inputs 197 | 198 | reset_GPIO: 199 | 200 | di ; stop any interrupts whle we configure 201 | 202 | ld a, $00 203 | out0 (PC_DR), a ; set DR of all port C to 0 204 | 205 | ld a, $FF 206 | out0 (PC_DDR), a ; set DDR of all port C to 0 207 | 208 | ld a, $00 209 | out0 (PC_ALT1), a ; set ALT1 of all port C to 0 210 | 211 | ld a, $00 212 | out0 (PC_ALT2), a ; set ALT2 of all port C to 0 213 | 214 | ei ; re-enable the interrupts 215 | 216 | ret 217 | 218 | ; --------------------------------------------- 219 | ; 220 | ; INTERRUPT SERVICE ROUTINES 221 | ; 222 | ; GPIO interrupt service routine for pins PC 0 & 1 223 | ; Triggered when status changes from high to low, or vice versa 224 | ; 225 | ; --------------------------------------------- 226 | 227 | isr_gpio_PC0_event: 228 | 229 | di ; stop any other interrupts while we do this 230 | push hl 231 | push af ; store any registers we are going to use 232 | 233 | in0 a, (PC_DR) ; read the current status of the GPIO PC pins 234 | ld (lastPortC), a ; store latest port data 235 | 236 | bit 0, a ; check status of the the GPIO pin we want (pin PC0) 237 | jr nz, @released ; decide if pressed or released 238 | 239 | @pressed: 240 | 241 | ld hl, msg_PC0_pressed ; put address of message into HL 242 | jr @end ; jump ahead 243 | 244 | @released: 245 | 246 | ld hl, msg_PC0_released ; put address of message into HL 247 | 248 | @end: 249 | or 00000011b ; only change bits 1 & 0, leave others as they are 250 | out0 (PC_DR), a ; set DR of port C to acknowledge interrupt 251 | 252 | TABTO 2,12 ; TAB to print text 253 | call printString ; print the message 254 | 255 | call displayPortC ; display purely for info, in binary format 256 | 257 | pop af 258 | pop hl ; retrieve registers we have used 259 | 260 | ei ; re-enable interrupts for next time 261 | reti.l ; return from interrupt routine 262 | 263 | 264 | ; --------------------------------------------- 265 | 266 | isr_gpio_PC1_event: 267 | 268 | di ; stop any other interrupts while we do this 269 | push hl 270 | push af ; store any registers we are going to use 271 | 272 | in0 a, (PC_DR) ; read the current status of the GPIO PC pins 273 | 274 | ld (lastPortC), a ; store latest port data 275 | bit 1, a ; check status of the the GPIO pin we want (pin PC1) 276 | jr nz, @released ; decide if pressed or released 277 | 278 | @pressed: 279 | 280 | ld hl, msg_PC1_pressed ; put address of message into HL 281 | jr @end ; jump ahead 282 | 283 | @released: 284 | 285 | ld hl, msg_PC1_released ; put address of message into HL 286 | 287 | @end: 288 | 289 | or 00000011b ; only change bits 1 & 0, leave others as they are 290 | out0 (PC_DR), a ; set DR of port C to acknowledge interrupt 291 | 292 | TABTO 2,14 ; TAB to print text 293 | call printString ; print the message 294 | 295 | call displayPortC ; display purely for info, in binary format 296 | 297 | pop af 298 | pop hl ; retrieve registers we have used 299 | 300 | ei ; re-enable interrupts for next time 301 | reti.l ; return from interrupt routine 302 | 303 | 304 | ; --------------------------------------------- 305 | 306 | displayPortC: 307 | 308 | TABTO 2,17 ; TAB to print text 309 | 310 | ld a, (lastPortC) ; retrieve the latest port value (need original value) 311 | call printBin ; display purely for info, in binary format 312 | 313 | ret 314 | 315 | lastPortC: .db 0 316 | 317 | ; --------------------------------------------- 318 | ; 319 | ; OTHER ROUTINES 320 | ; 321 | ; --------------------------------------------- 322 | 323 | ; print zero terminated string 324 | ; Any char apart from zero will be sent to VDP 325 | 326 | printString: 327 | ld a,(hl) ; put char byte into A 328 | or a ; check if zero 329 | ret z ; return if it is, as we are at the end of the zero terminated string 330 | rst.lil $10 ; send byte A to VDP 331 | inc hl ; move to next byte in the string 332 | jr printString ; loop around and test next byte 333 | 334 | ; --------------------------------------------- 335 | 336 | ; take A as number and print out as binary 337 | ; will destroy HL, BC 338 | 339 | printBin: 340 | push bc ; store BC for later 341 | ld b, 8 ; number of bits to do 342 | ld hl, binString 343 | @rpt: 344 | ld (hl), 48 ; set our byte to '0' as default 345 | 346 | bit 7, a ; check if bit is set 347 | jr z, @nxt ; if not, move on to next bit 348 | ld (hl), 49 ; set our byte to '1' 349 | @nxt: 350 | inc hl ; set next position in output 'binString' 351 | rla ; rotate byte A so that bit 7 is next bit 352 | djnz @rpt ; loop round until done 8 times 353 | 354 | ld hl, binString ; HL is address of the 'binary' string of 8 bytes 355 | call printString ; print the string at HL 356 | pop bc ; restore BC 357 | ret 358 | 359 | binString: .asciz "00000000" ; 8 chars plus a zero 360 | 361 | ; --------------------------------------------- 362 | ; 363 | ; text data 364 | ; 365 | ; --------------------------------------------- 366 | 367 | ; the title printed first 368 | msg_title: .asciz "GPIO Interrupt Handler " 369 | 370 | ; some messages with coloured text 371 | msg_PC0_pressed: .asciz 17, 15, "Pin PC0 ", 17, 13, "LOW (PRESSED) ", 17, 15 372 | msg_PC0_released: .asciz 17, 15, "Pin PC0 ", 17, 9, "HIGH (RELEASED) ", 17, 15 373 | msg_PC1_pressed: .asciz 17, 15, "Pin PC1 ", 17, 14, "LOW (PRESSED) ", 17, 15 374 | msg_PC1_released: .asciz 17, 15, "Pin PC1 ", 17, 12, "HIGH (RELEASED) ", 17, 15 375 | 376 | ; --------------------------------------------- 377 | ; 378 | ; END 379 | ; 380 | ; --------------------------------------------- 381 | -------------------------------------------------------------------------------- /guess_number.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/richardturnnidge/lessons/3cb69781f37a25d3ff7da6e31cd59a7ee4dc19b5/guess_number.bin -------------------------------------------------------------------------------- /guess_number_02.asm: -------------------------------------------------------------------------------- 1 | ; ------------------------------------ 2 | ; 3 | ; Number guessing game 4 | ; Richard Turnnidge 2024 5 | ; 6 | ; ------------------------------------ 7 | 8 | .assume adl=1 ; ez80 ADL memory mode 9 | .org $40000 ; load code here 10 | 11 | jp start_here ; jump to start of code 12 | 13 | .align 64 ; MOS header 14 | .db "MOS",0,1 15 | 16 | macro MOSCALL arg1 17 | ld a, arg1 18 | rst.lil $08 19 | endmacro 20 | 21 | debugging: equ 0 22 | 23 | start_here: 24 | 25 | push af ; store all the registers 26 | push bc 27 | push de 28 | push ix 29 | push iy 30 | 31 | ; ------------------ 32 | ; This is our actual code 33 | 34 | START: 35 | ld hl, msg_title ; print big title 36 | call printString 37 | 38 | MOSCALL $08 ; get IX pointer to sysvars 39 | ld a, (ix + $00) ; second counter in string 40 | ld (seed), a 41 | call get_random_byte_v2 ; set seed of rndv2 with clock data 42 | ld (randSeed),a ; set seed of randv1 with first random number 43 | 44 | GENERATING: 45 | ld a, 0 46 | ld (guesses), a 47 | call getRandom10 ; get a random number 1-10 48 | ld (guessNumber), a ; store for later 49 | 50 | if debugging 51 | call spitA ; give answer while debugging 52 | endif 53 | 54 | ld hl, msg_genNumber ; print msg 55 | call printString 56 | 57 | ld hl, msg_pressEnter 58 | call printString 59 | 60 | MOSCALL $1E ; get IX pointer to keyvals, currently pressed keys 61 | 62 | ENTER_TO_START: 63 | 64 | ld a, (ix + $09) ; ENTER code code 65 | bit 1, a 66 | jp nz, ENTER_NUMBER ; ENETR key to start 67 | jr ENTER_TO_START 68 | 69 | ENTER_NUMBER: 70 | 71 | ld hl, msg_guessNumber 72 | call printString 73 | 74 | 75 | 76 | GUESSHERE: 77 | 78 | ld a, (guesses) 79 | inc a 80 | ld (guesses),a 81 | 82 | ld hl, textBuffer ; HL needs to point to where text will be stored 83 | ld bc, 3 ; BC is maximum nunber of chars 84 | ld e, 1 ; 1 to clear buffer, 0 not to clear 85 | MOSCALL $09 ; call $09 mos_editline 86 | 87 | ld hl, LINEFEED 88 | call printString 89 | 90 | ld a, (guessNumber) ; get the random number to guess 91 | ld b,a 92 | ld hl, textBuffer 93 | call asc2int ; put decimal of input into A 94 | 95 | cp b 96 | jr z, CORRECT ; they were the same 97 | jr nc, TOO_HIGH 98 | 99 | TOO_LOW: 100 | 101 | ld hl, msg_tooLow 102 | call printString 103 | 104 | jr GUESS_AGAIN 105 | 106 | TOO_HIGH: 107 | 108 | ld hl, msg_tooHigh 109 | call printString 110 | 111 | GUESS_AGAIN: 112 | ld hl, msg_guessAgain 113 | call printString 114 | 115 | jp GUESSHERE 116 | 117 | CORRECT: 118 | 119 | ld hl, msg_guessIn 120 | call printString 121 | ld a, (guesses) 122 | call printDec 123 | ld hl, msg_guesstries 124 | call printString 125 | 126 | PLAY_AGAIN: 127 | 128 | ld hl, msg_again 129 | call printString 130 | MOSCALL $1E ; get IX pointer to keyvals, currently pressed keys 131 | 132 | WAIT_YN: 133 | 134 | ld a, (ix + $08) ; Y code code 135 | bit 4, a 136 | jp nz, PLAYAGAIN 137 | 138 | ld a, (ix + $0A) 139 | bit 5, a 140 | jp nz, NOPLAY ; N code 141 | 142 | jr WAIT_YN 143 | 144 | PLAYAGAIN: 145 | ld a, 'y' 146 | rst.lil $10 ; print a 'y' 147 | jp GENERATING 148 | 149 | NOPLAY: 150 | 151 | ld hl, msg_OK 152 | call printString 153 | 154 | ; ------------------ 155 | ; This is where we exit the program 156 | 157 | EXIT_HERE: 158 | 159 | pop iy ; Pop all registers back from the stack 160 | pop ix 161 | pop de 162 | pop bc 163 | pop af 164 | ld hl,0 ; Load the MOS API return code (0) for no errors. 165 | ret ; Return to MOS 166 | 167 | ; ------------------ 168 | 169 | 170 | msg_pressEnter: .asciz "Press ENTER to start!\r\n\r\n" 171 | msg_genNumber: .asciz "\r\nGenerating a new number!\r\n\r\n" 172 | msg_guessNumber: .asciz "Guess a number between 1 and 10: " 173 | msg_guessAgain: .asciz "Guess again: " 174 | msg_tooLow: .asciz "too low!\r\n" 175 | msg_tooHigh: .asciz "too high!\r\n" 176 | msg_again: .asciz "Do you want to play again? (y/n): " 177 | msg_OK: .asciz "n\r\n\r\nOkay\r\n", 17,15 178 | msg_guessIn: .asciz "Congratulations!\r\n\r\nYou guessed the number in " 179 | msg_guesstries: .asciz " tries!\r\n\r\n" 180 | 181 | ; title printing routine could be made more byte efficient 182 | msg_title: .db 17, 10 183 | msg_title0: .db " # # #### ###\r\n" 184 | msg_title1: .db " ## # # # # # #### ##### #### # # # # ##### ### ### ###\r\n" 185 | msg_title2: .db " # # # # # ## ## # # # # # # # # # # # ###\r\n" 186 | msg_title3: .db " # # # # # # ## # #### #### # # # ### # # #### ### ### #\r\n" 187 | msg_title4: .db " # # # # # # # # # # #### # # # # # # #\r\n" 188 | msg_title5: .db " # ## # # # # # # # # # # # # # # # # # # ###\r\n" 189 | msg_title6: .db " # # ### # # #### ##### # # #### ### ##### ### ### ###\r\n",0 190 | 191 | LINEFEED: .asciz "\r\n" 192 | 193 | 194 | textBuffer: .blkb 3,0 ; buffer for input bar 195 | 196 | guessNumber: .db 0 ; the number to guess 197 | guesses: .db 0 ; the numnber of guesses made 198 | 199 | ; --------------------------------------------- 200 | 201 | printString: ; print zero terminated string 202 | ld a,(hl) 203 | or a 204 | ret z 205 | RST.LIL 10h 206 | inc hl 207 | jr printString 208 | 209 | 210 | ; --------------------------------------------- 211 | ; routine to get random number 1-10 212 | 213 | getRandom10: 214 | 215 | call get_random_byte ; get first byte 216 | and 00000111b ; just 0-7 217 | 218 | ld b, a ; store first part in b 219 | call get_random_byte ; get first byte 220 | rla 221 | rla 222 | and 00000001b ; just 0-1 223 | 224 | add a, b ; add first part to get nmber 0-9 225 | inc a ; inc so 1-10 226 | 227 | ret 228 | 229 | ; --------------------------------------------- 230 | 231 | get_random_byte: ;returns A as random byte 232 | .db 3Eh ;start of ld a,* 233 | randSeed: 234 | .db 0 235 | push bc 236 | 237 | ld c,a 238 | add a,a 239 | add a,c 240 | add a,a 241 | add a,a 242 | add a,c 243 | add a,83 244 | ld (randSeed),a 245 | pop bc 246 | ret 247 | 248 | ; --------------------------------------------- 249 | ; alternate random number generator 250 | 251 | get_random_byte_v2: 252 | 253 | ld a, (seed) 254 | ld b, a 255 | 256 | rrca ; multiply by 32 257 | rrca 258 | rrca 259 | xor 0x1f 260 | 261 | add a, b 262 | sbc a, 255 ; carry 263 | 264 | ld (seed), a 265 | ret 266 | 267 | seed: 268 | .db 255 269 | 270 | ; --------------------------------------------- 271 | ; simple assumption value is 1-10 272 | ; take entry pointer as HL 273 | ; which will be 10z or Az 274 | 275 | asc2int: 276 | inc hl 277 | ld a, (hl) ; this will be 0 or an ascii char 278 | cp 0 279 | jr z, @under10 ; was terminated after 1 byte 280 | 281 | @over 10: 282 | ld a, 10 283 | ret 284 | 285 | @under10: 286 | dec hl 287 | ld a, (hl) ; this will be 0 or an ascii char 288 | sub 48 ; sub 48 fropm ascii to dec 289 | ret 290 | 291 | 292 | ; --------------------------------------------- 293 | if debugging 294 | spitA: ; debug A to screen as HEX byte pair at pos BC 295 | push af 296 | ld (debug_char), a ; store A 297 | ; first, print 'A=' at TAB 36,0 298 | ld hl, LINEFEED 299 | call printString 300 | 301 | ld a, (debug_char) ; get A from store, then split into two nibbles 302 | and 11110000b ; get higher nibble 303 | rra 304 | rra 305 | rra 306 | rra ; move across to lower nibble 307 | add a,48 ; increase to ascii code range 0-9 308 | cp 58 ; is A less than 10? (58+) 309 | jr c, @next ; carry on if less 310 | add a, 7 ; add to get 'A' char if larger than 10 311 | @next: 312 | rst.lil $10 ; print the A char 313 | 314 | ld a, (debug_char) ; get A back again 315 | and 00001111b ; now just get lower nibble 316 | add a,48 ; increase to ascii code range 0-9 317 | cp 58 ; is A less than 10 (58+) 318 | jp c, @next2 ; carry on if less 319 | add a, 7 ; add to get 'A' char if larger than 10 320 | @next2: 321 | rst.lil $10 ; print the A char 322 | 323 | ld a, (debug_char) 324 | pop af 325 | ret ; head back 326 | 327 | debug_char: .db 0 328 | endif 329 | ; --------------------------------- 330 | 331 | printDec: ; debug A to screen as 3 char string pos 332 | 333 | push af 334 | ld a, 48 335 | ld (answer),a 336 | ld (answer+1),a 337 | ld (answer+2),a ; reset to default before starting 338 | 339 | ; is it bigger than 200? 340 | pop af 341 | 342 | ld (base),a ; save 343 | 344 | cp 200 345 | jr c,_under200 ; not 200+ 346 | sub a, 200 347 | ld (base),a ; sub 200 and save 348 | 349 | ld a, 50 ; 2 in ascii 350 | rst.lil $10 ; print out a '200' digit 351 | ld (answer),a 352 | jr _under100 353 | 354 | _under200: 355 | cp 100 356 | jr c,_under100 ; not 200+ 357 | sub a, 100 358 | ld (base),a ; sub 200 and save 359 | 360 | ld a, 49 ; 1 in ascii 361 | ld (answer),a 362 | 363 | rst.lil $10 ; print out a '100' digit 364 | jr _under100 365 | 366 | 367 | _under100: 368 | ld a, (base) 369 | ld c,a 370 | ld d, 10 371 | call C_Div_D 372 | 373 | add a, 48 374 | ld (answer + 2),a 375 | ld b, a 376 | 377 | ld a, c 378 | cp 0 379 | jr z, _lastDigit 380 | 381 | add a, 48 382 | ld (answer + 1),a 383 | rst.lil $10 ; print out 10s digit 384 | _lastDigit: 385 | ld a,b 386 | rst.lil $10 ; print out last digit 387 | 388 | ret 389 | 390 | 391 | debugOut: 392 | answer: .db "000" ; string to output 393 | endDebugOut: 394 | 395 | base: .db 0 ; used in calculations 396 | 397 | ; ----------------- 398 | 399 | C_Div_D: 400 | ;Inputs: 401 | ; C is the numerator 402 | ; D is the denominator 403 | ;Outputs: 404 | ; A is the remainder 405 | ; B is 0 406 | ; C is the result of C/D 407 | ; D,E,H,L are not changed 408 | ; 409 | ld b,8 410 | xor a 411 | sla c 412 | rla 413 | cp d 414 | jr c,$+4 415 | inc c 416 | sub d 417 | djnz $-8 418 | ret 419 | 420 | 421 | ; --------------------------------- 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 435 | 436 | 437 | 438 | 439 | 440 | 441 | 442 | 443 | 444 | 445 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | 461 | 462 | 463 | 464 | 465 | 466 | 467 | 468 | -------------------------------------------------------------------------------- /hello.asm: -------------------------------------------------------------------------------- 1 | .assume adl=1 ; ez80 ADL memory mode 2 | .org $40000 ; load code here 3 | 4 | jp start_here ; jump to start of code 5 | 6 | .align 64 ; MOS header 7 | .db "MOS",0,1 8 | 9 | 10 | start_here: 11 | 12 | push af ; store all the registers 13 | push bc 14 | push de 15 | push ix 16 | push iy 17 | 18 | ; ------------------ 19 | ; This is our actual code 20 | 21 | ld hl, string ; address of string to use 22 | ld bc,0 ; length of string, or 0 if a delimiter is used 23 | ld a,0 ; A is the delimiter 24 | rst.lil $18 ; Call the MOS API to send data to VDP 25 | 26 | ; ------------------ 27 | ; This is where we exit the program 28 | 29 | pop iy ; Pop all registers back from the stack 30 | pop ix 31 | pop de 32 | pop bc 33 | pop af 34 | ld hl,0 ; Load the MOS API return code (0) for no errors. 35 | ret ; Return to MOS 36 | 37 | ; ------------------ 38 | 39 | string: 40 | .db "Hello Agon World\r\n",0 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /ink.asm: -------------------------------------------------------------------------------- 1 | ; Example of MOSlet 2 | 3 | .assume adl=1 4 | .org $0B0000 ; NOTE different assemble address for MOSlets 5 | 6 | jp start ; We jump over the header 7 | 8 | .align 64 9 | .db "MOS" 10 | .db 00h 11 | .db 01h 12 | 13 | app_name: .db "ink.bin", 0 ; The executable name, only used in arg1 14 | max_args: EQU 16 ; Maximum number of arguments allowed in argv 15 | arg_pointer: .blkb max_args * 3, 0 ; max 16 x 3 bytes each 16 | num_args: .db 0 ; the number of arguments entered 17 | 18 | ; --------------------------------------------- 19 | ; 20 | ; INITIAL SETUP CODE HERE 21 | ; 22 | ; --------------------------------------------- 23 | 24 | start: 25 | push af ; Push all registers to the stack 26 | push bc 27 | push de 28 | push ix 29 | push iy 30 | 31 | ld IX, arg_pointer ; The argv array pointer address 32 | push IX 33 | call PARSE_PARAMS ; Parse the parameters 34 | ld a,c ; C contains the number of params entered 35 | ld (num_args),a ; store number of arguments entered 36 | pop IX ; IX: argv 37 | 38 | cp 2 39 | jr c, error ; if less than 2 args, then exit 40 | ld de, (ix+3) ; address of first arg, is a 0 terminated string for the file 41 | 42 | call STRING2INT ; turn string into a integer we can use 43 | ; hl = result, de = pointer to ASCII number 44 | ; no error checking, so watch out! 45 | ; lower byte L will contain the integer we want to use 46 | ld a, 17 47 | rst.lil $10 ; change ink colour to... 48 | ld a, l 49 | rst.lil $10 ; the value we got back in L 50 | 51 | now_exit: 52 | 53 | pop iy ; Pop all registers back from the stack 54 | pop ix 55 | pop de 56 | pop bc 57 | pop af 58 | ld hl,0 ; Load the MOS API return code (0) for no errors. 59 | 60 | ret ; Return MOS 61 | 62 | error: 63 | ld hl, errorString ; location of string 64 | call printString 65 | jp now_exit 66 | 67 | printString: ; print zero terminated string 68 | ld a,(hl) 69 | or a 70 | ret z 71 | RST.LIL 10h 72 | inc hl 73 | jr printString 74 | 75 | errorString: 76 | .db "ERROR - no value entered\n\r",0 77 | 78 | ; --------------------------------------------- 79 | ; 80 | ; PARAM PARSING ROUTINE WRITTEN BY OTHERS 81 | ; 82 | ; --------------------------------------------- 83 | 84 | ; Parse the parameter string into a C style array 85 | ; Parameters 86 | ; - HL: Address of parameter string 87 | ; - IX: Address for array pointer storage 88 | ; Returns: 89 | ; - C: Number of parameters parsed 90 | 91 | PARSE_PARAMS: 92 | ld BC, app_name 93 | ld (IX+0), BC ; ARGV[0] = the executable name 94 | inc IX 95 | inc IX 96 | inc IX 97 | call skip_spaces ; Skip HL past any leading spaces 98 | 99 | ld BC, 1 ; C: ARGC = 1 - also clears out top 16 bits of BCU 100 | ld B, max_args - 1 ; B: Maximum number of arg_pointer 101 | 102 | parse_step_2: 103 | push BC ; Stack ARGC 104 | push HL ; Stack start address of token 105 | call get_token ; Get the next token 106 | ld A, C ; A: Length of the token in characters 107 | pop DE ; Start address of token (was in HL) 108 | pop BC ; ARGC 109 | or A ; Check for A=0 (no token found) OR at end of string 110 | ret Z 111 | 112 | ld (IX+0), DE ; Store the pointer to the token 113 | push HL ; DE=HL 114 | pop DE 115 | call skip_spaces ; And skip HL past any spaces onto the next character 116 | Xor A 117 | ld (DE), A ; Zero-terminate the token 118 | inc IX 119 | inc IX 120 | inc IX ; Advance to next pointer position 121 | inc C ; Increment ARGC 122 | ld A, C ; Check for C >= A 123 | cp B 124 | jr C, parse_step_2 ; And loop 125 | RET 126 | 127 | ; --------------------------------------------- 128 | 129 | ; Skip spaces in the parameter string 130 | ; Parameters: 131 | ; - HL: Address of parameter string 132 | ; Returns: 133 | ; - HL: Address of next none-space character 134 | ; F: Z if at end of string, otherwise NZ if there are more tokens to be parsed 135 | 136 | skip_spaces: 137 | ld A, (HL) ; Get the character from the parameter string 138 | cp ' ' ; Exit if not space 139 | ret NZ 140 | inc HL ; Advance to next character 141 | jr skip_spaces ; Increment length 142 | 143 | 144 | ; --------------------------------------------- 145 | 146 | ; Get the next token 147 | ; Parameters: 148 | ; - HL: Address of parameter string 149 | ; Returns: 150 | ; - HL: Address of first character after token 151 | ; - C: Length of token (in characters) 152 | 153 | get_token: 154 | ld C, 0 ; Initialise length 155 | token_loop: 156 | ld A, (HL) ; Get the character from the parameter string 157 | or A ; Exit if 0 (end of parameter string in MOS) 158 | ret Z 159 | cp 13 ; Exit if CR (end of parameter string in BBC BASIC) 160 | ret Z 161 | cp ' ' ; Exit if space (end of token) 162 | ret Z 163 | inc HL ; Advance to next character 164 | inc C ; Increment length 165 | jr token_loop 166 | 167 | 168 | ; --------------------------------------------- 169 | ; 170 | ; STRING TO INTEGER ROUTINE WRITTEN BY OTHERS 171 | ; 172 | ; --------------------------------------------- 173 | 174 | ; Takes pointer to a string of ascii representing an integer and converts to 3 byte integer 175 | ; hl = result 176 | ; de = pointer to ASCII number 177 | 178 | STRING2INT: 179 | Ld hl,0 180 | s2i_loop: 181 | ld a,(de) 182 | Sub 48 183 | Jr c,s2i_done 184 | Cp 10 185 | Jr nc,s2i_done 186 | Push hl 187 | Pop bc 188 | Add hl,hl ; x2 189 | Add hl,hl ; x4 190 | Add hl,bc ; x5 191 | Add hl,hl ; x10 192 | Ld bc,0 193 | Ld c,a 194 | Add hl,bc ; Add digit 195 | Inc de ; go to next number 196 | Jr s2i_loop 197 | s2i_done: 198 | ret 199 | 200 | ; --------------------------------------------- 201 | 202 | 203 | 204 | 205 | -------------------------------------------------------------------------------- /interrupts_UART_1.asm: -------------------------------------------------------------------------------- 1 | ; AGON UART INTERRUPTS 2 | ; Richard Turnnidge 2025 3 | ; UART1 text receiver example 4 | 5 | ; --------------------------------------------- 6 | ; 7 | ; MACROS 8 | ; 9 | ; --------------------------------------------- 10 | 11 | macro MOSCALL function 12 | ld a, function 13 | rst.lil $08 14 | endmacro 15 | 16 | ; --------------------------------------------- 17 | 18 | macro TABTO x, y 19 | ld a, 31 20 | rst.lil $10 21 | ld a, x 22 | rst.lil $10 23 | ld a, y 24 | rst.lil $10 25 | endmacro 26 | 27 | ; --------------------------------------------- 28 | 29 | macro CLS 30 | ld a, 12 31 | rst.lil $10 32 | endmacro 33 | 34 | ; --------------------------------------------- 35 | 36 | macro SETCURSOR value ; to set cursor visible or not [0 or 1] 37 | push af 38 | ld a, 23 39 | rst.lil $10 40 | ld a, 1 41 | rst.lil $10 42 | ld a, value 43 | rst.lil $10 ; VDU 23,1,value [0 off, 1 on] 44 | pop af 45 | endmacro 46 | 47 | ; --------------------------------------------- 48 | ; 49 | ; CONSTANTS 50 | ; 51 | ; --------------------------------------------- 52 | 53 | receiveBuffer: EQU $D0 ; Receive buffer port ID 54 | 55 | ; for UART1_Struct 56 | UART1_BAUD: EQU 31250;9600 ; baud_rate (stored as three byte LONG) 31250 for midi 57 | UART1_DATABITS: EQU 8 ; data bits 58 | UART1_STOPBITS: EQU 1 ; stop bits 59 | UART1_PARITY: EQU 0 ; parity bits 60 | UART1_FLOW: EQU 0 ; flow control 61 | UART1_INTERRUPT: EQU 00000001b ; interrupt enabled (bit 1) 62 | 63 | ; interrupt bits: - Bit 0: Set to enable received data interrupt 64 | ; - Bit 1: Set to enable transmit data interrupt 65 | ; - Bit 2: Set to enable line status change interrupt 66 | ; - Bit 3: Set to enable modem status change interrupt 67 | ; - Bit 4: Set to enable transmit complete interrupt 68 | 69 | ; --------------------------------------------- 70 | ; 71 | ; INITIALISE 72 | ; 73 | ; --------------------------------------------- 74 | 75 | .assume adl=1 ; big memory mode 76 | .org $40000 ; load code here 77 | 78 | jp start_here ; jump to start of code 79 | 80 | .align 64 ; MOS header 81 | .db "MOS",0,1 82 | 83 | ; --------------------------------------------- 84 | ; 85 | ; INITIAL SETUP CODE HERE 86 | ; 87 | ; --------------------------------------------- 88 | 89 | start_here: 90 | ; store everything as good practice 91 | push af ; pop back when we return from code later 92 | push bc 93 | push de 94 | push ix 95 | push iy 96 | 97 | im 2 98 | 99 | ld a, 0 100 | ld (byte_count),a 101 | 102 | SETCURSOR 0 103 | call reset_buffer 104 | CLS 105 | call openUART1 ; open serial port for comms 106 | 107 | ; --------------------------------------------- 108 | ; 109 | ; MAIN LOOP 110 | ; 111 | ; --------------------------------------------- 112 | 113 | MAIN_LOOP: 114 | MOSCALL $08 ; get IX pointer to sysvars 115 | ld a, (ix + 05h) ; ix+5h is 'last key pressed' 116 | cp 27 ; is it ESC key? 117 | jp z, exit_here ; if so exit cleanly 118 | 119 | call display_buffer ; print the buffer and char count 120 | 121 | jr MAIN_LOOP 122 | 123 | ; --------------------------------------------- 124 | 125 | display_buffer: ; display the buffer contents 126 | 127 | TABTO 0, 0 ; set start position 128 | ld hl, msg_count ; print "Count:" 129 | call printString 130 | 131 | ld a, (byte_count) ; number of bytes received so far 132 | call printDec ; print count in decimal 133 | 134 | ld hl, LINEFEED ; address of newline char sequence 135 | call printString ; print a new line 136 | ld hl, LINEFEED ; address of newline char sequence 137 | call printString ; print a new line 138 | 139 | ld de, uart_buffer ; HL is start of received byte buffer 140 | ld b, 24 ; will go through first 24 bytes only in this example 141 | 142 | @loop: 143 | 144 | ld hl, clearLine 145 | call printString 146 | 147 | ld a, 00000010b 148 | call multiPurposeDelay 149 | 150 | ld a, (de) ; get buffer byte 151 | inc de ; inc buffer offset for next time 152 | push bc ; store B counter 153 | push de ; store position into buffer 154 | push af ; store our byte 155 | 156 | cp 31 ; compare with 31 (last of non-printing ascii codes) 157 | jr nc, @print_char ; if <32 (non-printing), we just print an '?' 158 | ld a, '?' ; replace with '?' 159 | 160 | @print_char: 161 | rst.lil $10 ; just print the byte A 162 | 163 | ld a, ' ' ; put SPACE byte into A 164 | rst.lil $10 ; print a space for readability on screen 165 | 166 | pop af ; get our original byte back 167 | call printDec ; print decimal of byte 168 | 169 | ld hl, LINEFEED ; address of newline char sequence 170 | call printString ; print a new line 171 | 172 | pop de ; get buffer offset position back 173 | pop bc ; get B counter back 174 | djnz @loop ; go round again if not done all 24 175 | 176 | ret 177 | 178 | ; --------------------------------------------- 179 | ; 180 | ; EXIT CODE CLEANLY 181 | ; 182 | ; --------------------------------------------- 183 | 184 | exit_here: 185 | SETCURSOR 1 186 | call closeUART1 187 | CLS 188 | 189 | pop iy 190 | pop ix 191 | pop de 192 | pop bc 193 | pop af 194 | ld hl,0 195 | 196 | ret ; return to MOS here 197 | 198 | ; --------------------------------------------- 199 | ; 200 | ; UART ROUTINES 201 | ; 202 | ; --------------------------------------------- 203 | 204 | openUART1: 205 | ld ix, UART1_Struct ; set IX to be the address of the STRUCT 206 | MOSCALL $15 ; open uart1 207 | 208 | ld hl, uart_isr_handler ; address of interrupt service routine 209 | ld e, $1A ; interrupt vector number (UART1) 210 | MOSCALL $14 ; mos_api_setintvector 211 | 212 | ld hl, uart_buffer 213 | ld (uart_buf_pos), hl ; set curent buffer write position 214 | 215 | ret 216 | 217 | ; --------------------------------------------- 218 | 219 | closeUART1: 220 | MOSCALL $16 ; close uart1 221 | ret 222 | 223 | ; --------------------------------------------- 224 | 225 | UART1_Struct: ; constants defined at start of program 226 | .dl UART1_BAUD ; baud rate (stored as three byte LONG) 227 | .db UART1_DATABITS ; data bits 228 | .db UART1_STOPBITS ; stop bits 229 | .db UART1_PARITY ; parity bits 230 | .db UART1_FLOW ; flow control 231 | .db UART1_INTERRUPT ; interrupt bits - bit 0 is enable received data interrupt 232 | 233 | ; --------------------------------------------- 234 | 235 | uart_isr_handler: ; this is the interrupt service routine (ISR) that gets called 236 | di ; stop any other interrupts while we do this 237 | push af 238 | push hl ; store any registers we are going to use 239 | 240 | ; do ISR code here 241 | 242 | in0 a,(receiveBuffer) ; read byte from buffer 243 | 244 | ld hl,(uart_buf_pos) ; get curent buffer write position 245 | ld (hl), a ; store byte received 246 | inc hl ; inc for next time 247 | ld (uart_buf_pos), hl ; store pointer to buffer position 248 | 249 | ld a, (byte_count) 250 | inc a 251 | ld (byte_count),a ; just counting the bytes coming in 252 | 253 | pop hl 254 | pop af ; return register values 255 | ei ; enable interrupts again 256 | 257 | reti.l ; return from ISR 258 | 259 | byte_count: .db 0 ; count of bytes received, just for testing 260 | uart_buf_pos: .dl 0 ; current position into buffer memory 261 | 262 | 263 | ; --------------------------------------------- 264 | ; 265 | ; OTHER ROUTINES 266 | ; 267 | ; --------------------------------------------- 268 | 269 | ; print decimal value of 0 -> 255 to screen at current TAB position 270 | 271 | printDec: ; debug A to screen as 3 char string pos 272 | 273 | ld (base),a ; save 274 | 275 | cp 200 ; are we under 200 ? 276 | jr c,@under200 ; not 200+ 277 | sub a, 200 278 | ld (base),a ; sub 200 and save 279 | 280 | ld a, '2' ; 2 in ascii 281 | rst.lil $10 ; print out a '200' digit 282 | 283 | jr @under100 284 | 285 | @under200: 286 | cp 100 ; are we under 100 ? 287 | jr c,@under100 ; not 200+ 288 | sub a, 100 289 | ld (base),a ; sub 100 and save 290 | 291 | ld a, '1' ; 1 in ascii 292 | rst.lil $10 ; print out a '100' digit 293 | 294 | @under100: 295 | ld a, (base) ; get last 2 digits as decimal 296 | ld c,a ; store numerator in C 297 | ld d, 10 ; D will be denominator 298 | call C_Div_D ; divide C by 10 to get two parts. 299 | ; A is the remainder, C is the int of C/D 300 | 301 | ld b, a ; put remainder ascii into B 302 | 303 | ld a, c ; get int div 304 | cp 0 ; if 0 (ie, number was <10) 305 | jr z, @lastBut1 ; just do last digit 306 | 307 | add a, 48 ; add 48 to make ascii of int C/D 308 | rst.lil $10 ; print out 10s digit 309 | jr @lastDigit 310 | 311 | @lastBut1: 312 | add a, 48 ; add 48 to make ascii of int C/D 313 | rst.lil $10 ; print out 10s digit 314 | 315 | @lastDigit: 316 | ld a,b ; get remainder back 317 | add a, 48 ; add 48 to remainder to convert to ascii 318 | rst.lil $10 ; print out last digit 319 | 320 | ret 321 | 322 | base: .db 0 ; used in calculations 323 | 324 | ; --------------------------------------------- 325 | 326 | C_Div_D: 327 | ;Inputs: 328 | ; C is the numerator 329 | ; D is the denominator 330 | ;Outputs: 331 | ; A is the remainder 332 | ; B is 0 333 | ; C is the result of C/D 334 | ; D,E,H,L are not changed 335 | ; 336 | ld b,8 ; B is counter = 8 337 | xor a ; [loop] clear flags 338 | sla c ; C = C x 2 339 | rla ; A = A x 2 + Carry 340 | cp d ; compare A with Denominator 341 | jr c,$+4 ; if bigger go to loop 342 | inc c ; inc Numerator 343 | sub d ; A = A - denominator 344 | djnz $-8 ; go round loop 345 | ret ; done 8 times, so return 346 | 347 | ; --------------------------------------------- 348 | 349 | ; print zero terminated string 350 | ; Any char apart from zero will be sent to VDP 351 | 352 | printString: 353 | ld a,(hl) ; put char byte into A 354 | or a ; check if zero 355 | ret z ; return if it is, as we are at the end of the zero terminated string 356 | rst.lil $10 ; send byte A to VDP 357 | inc hl ; move to next byte in the string 358 | jr printString ; loop around and test next byte 359 | 360 | ; --------------------------------------------- 361 | 362 | reset_buffer: 363 | ld hl, blank 364 | ld de, uart_buffer 365 | ld bc, 24 366 | ldir 367 | ret 368 | 369 | ; --------------------------------------------- 370 | ; routine waits a fixed time, then returns 371 | 372 | multiPurposeDelay: 373 | push bc 374 | 375 | ; arrive with A = the delay byte. One bit to be set only. 376 | ld b, a 377 | MOSCALL $08 ; get IX pointer to sysvars 378 | 379 | waitLoop: 380 | 381 | ld a, (ix + 0) ; ix+0h is lowest byte of clock timer 382 | 383 | ; need to check if bit set is same as last time we checked. 384 | ; bit 0 - changes 128 times per second 385 | ; bit 1 - changes 64 times per second 386 | ; bit 2 - changes 32 times per second 387 | ; bit 3 - changes 16 times per second 388 | 389 | ; bit 4 - changes 8 times per second 390 | ; bit 5 - changes 4 times per second 391 | ; bit 6 - changes 2 times per second 392 | ; bit 7 - changes 1 times per second 393 | ; eg. and 00000010b ; check 1 bit only 394 | and b 395 | ld c,a 396 | ld a, (oldTimeStamp) 397 | cp c ; is A same as last value? 398 | jr z, waitLoop ; loop here if it is 399 | ld a, c 400 | ld (oldTimeStamp), a ; set new value 401 | 402 | pop bc 403 | ret 404 | 405 | oldTimeStamp: .db 00h 406 | 407 | ; --------------------------------------------- 408 | clearLine: .asciz " ",8,8,8,8,8 ; 5 spaces then 5 backspaces 409 | msg_count: .asciz "Count:" 410 | LINEFEED: .asciz "\r\n" 411 | blank: .db ' ' 412 | uart_buffer: 413 | .db " " 414 | 415 | ; --------------------------------------------- 416 | ; 417 | ; END 418 | ; 419 | ; --------------------------------------------- 420 | 421 | 422 | 423 | 424 | 425 | -------------------------------------------------------------------------------- /interrupts_timer.asm: -------------------------------------------------------------------------------- 1 | ; --------------------------------------------- 2 | ; 3 | ; MACROS 4 | ; 5 | ; --------------------------------------------- 6 | 7 | macro MOSCALL function 8 | ld a, function 9 | rst.lil $08 10 | endmacro 11 | 12 | ; --------------------------------------------- 13 | 14 | macro TABTO x, y 15 | ld a, 31 16 | rst.lil $10 17 | ld a, x 18 | rst.lil $10 19 | ld a, y 20 | rst.lil $10 21 | endmacro 22 | 23 | ; --------------------------------------------- 24 | 25 | macro CLS 26 | ld a, 12 27 | rst.lil $10 28 | endmacro 29 | 30 | ; --------------------------------------------- 31 | 32 | macro SETCURSOR value ; to set cursor visible or not [0 or 1] 33 | push af 34 | ld a, 23 35 | rst.lil $10 36 | ld a, 1 37 | rst.lil $10 38 | ld a, value 39 | rst.lil $10 ; VDU 23,1,value [0 off, 1 on] 40 | pop af 41 | endmacro 42 | 43 | ; --------------------------------------------- 44 | ; 45 | ; INITIALISE AGON 46 | ; 47 | ; --------------------------------------------- 48 | 49 | .assume adl=1 ; big memory mode 50 | .org $40000 ; load code here 51 | 52 | jp start_here ; jump to start of code 53 | 54 | .align 64 ; MOS header 55 | .db "MOS",0,1 56 | 57 | ; --------------------------------------------- 58 | ; 59 | ; INITIAL SETUP CODE HERE 60 | ; 61 | ; --------------------------------------------- 62 | 63 | start_here: 64 | ; store everything as good practice 65 | push af ; we will pop back when we return from code later 66 | push bc 67 | push de 68 | push ix 69 | push iy 70 | 71 | im 2 ; make sure running in interrupt mode 2 72 | 73 | CLS ; clear screen 74 | SETCURSOR 0 ; huide cursor 75 | 76 | TABTO 2,8 ; TAB to position on screen 77 | ld hl, msg ; get pointer to message to print 78 | call printString ; print the demo title 79 | 80 | ld hl,0 ; put 0 into HL 81 | ld (prt_irq_counter),hl ; reset the counter we display 82 | 83 | ld a, 10 ; freq of timer 84 | ld (prt_freq), a ; store it 85 | call printHex ; print the freq 86 | 87 | ld hl, msg2 ; get pointer to message to print 88 | call printString ; print 'Hz' 89 | 90 | ; the next calls set up the timer 91 | 92 | call stop_timer ; disable timer in case already running (by another program) 93 | 94 | call set_timer_freq ; config timer frequency 95 | 96 | ld hl, timer_isr ; HL = address of service routine to call 97 | call set_timer_ISR ; define ISR address 98 | 99 | call start_timer ; start the timer interrupt going 100 | 101 | ; --------------------------------------------- 102 | ; 103 | ; MAIN LOOP 104 | ; 105 | ; --------------------------------------------- 106 | 107 | MAIN_LOOP: 108 | 109 | MOSCALL $08 ; get IX pointer to sysvars 110 | ld a, (ix + 05h) ; ix+5h is 'last key pressed' 111 | cp 27 ; is it ESC key? 112 | jp z, exit_here ; if so exit cleanly 113 | 114 | ; all we do here is wait until ESC key is pressed 115 | ; all other updates are in the interrupt service routine 116 | 117 | jr MAIN_LOOP 118 | 119 | ; --------------------------------------------- 120 | ; 121 | ; EXIT CODE CLEANLY 122 | ; 123 | ; --------------------------------------------- 124 | 125 | exit_here: 126 | 127 | 128 | call stop_timer ; disable timer so it doesn't keep running after we exit! 129 | 130 | CLS 131 | SETCURSOR 1 132 | 133 | pop iy 134 | pop ix 135 | pop de 136 | pop bc 137 | pop af 138 | ld hl,0 139 | 140 | ret ; return to MOS here 141 | 142 | 143 | 144 | ; --------------------------------------------- 145 | ; 146 | ; INTERRUPT TIMER ROUTINES - using timer id 1 (TMR1_CTL) 147 | ; 148 | ; --------------------------------------------- 149 | 150 | ; start the timer interrupt 151 | 152 | start_timer: 153 | 154 | ; enable timer, with interrupt and CONTINUOUS mode, clock divider 256 155 | ld a, PRT_IRQ_0 | IRQ_EN_1 | PRT_MODE_1 | CLK_DIV_256 | RST_EN_1 | PRT_EN_1 ; define settings 156 | out0 (TMR1_CTL),a ; set the timer 157 | 158 | ret 159 | 160 | ; --------------------------------------------- 161 | ; set the timer's interrupt service routine. 162 | ; Arrive with HL = ISR address (timer_isr in this example) 163 | 164 | set_timer_ISR: 165 | 166 | ld e, $0C ; interrupt number (PRT 1) 167 | MOSCALL $14 ; mos_api_setintvector 168 | 169 | ret 170 | 171 | ; --------------------------------------------- 172 | ; set the timer's frequency 173 | 174 | set_timer_freq: 175 | ld a, (prt_freq) ; get frequency wanted 176 | ld c, a ; put into C for division code 177 | 178 | ; now work out the reload values, based on C being given as frequency/sec 179 | ld hl, prt_reload_default ; this is the reload timer default value 180 | call HL_Div_C ; C is freq we want to calulate 181 | ; HL is returned with new value 182 | 183 | ; set reload values into the PRT registers 184 | out0 (TMR1_RR_L),l ; L is low byte of timer 185 | out0 (TMR1_RR_H),h ; H is high byte of timer 186 | 187 | ret 188 | 189 | ; --------------------------------------------- 190 | ; stop the timer interrupt 191 | 192 | stop_timer: 193 | 194 | ; disable timer 195 | ld a, PRT_IRQ_0 | IRQ_EN_0 | PRT_MODE_0 | CLK_DIV_256 | RST_EN_1 | PRT_EN_0 ; define settings 196 | out0 (TMR1_CTL),a ; set the timer 197 | 198 | ret 199 | 200 | ; --------------------------------------------- 201 | ; Programmable Reload Timer - interrupt service routine gets called at pre-defined frequency 202 | 203 | timer_isr: 204 | 205 | di ; stop any other interrupts while we do this 206 | push af 207 | push hl ; store any registers we are going to use 208 | 209 | ; do ISR code here 210 | in0 a,(TMR1_CTL) ; reset current interrupt. 211 | 212 | ld hl, (prt_irq_counter) ; get current counter 213 | inc hl ; inc clounter 214 | ld (prt_irq_counter),hl ; store counter 215 | 216 | 217 | TABTO 2,10 ; TAB to screen position 218 | ld hl,(prt_irq_counter) ; get current counter 219 | ld a, h ; put high byte into A 220 | call printHex ; print value 221 | 222 | TABTO 4,10 ; TAB to screen position 223 | ld hl,(prt_irq_counter) ; get current counter 224 | ld a, l ; put low byte into A 225 | call printHex ; print value 226 | 227 | pop hl 228 | pop af ; return register values 229 | 230 | ei ; enable interrupts again 231 | 232 | reti.l ; return from interrupt routine 233 | 234 | ; --------------------------------------------- 235 | ; 236 | ; OTHER FUNCTIONS 237 | ; 238 | ; --------------------------------------------- 239 | 240 | ; 24 bit division by 8 bit value 241 | ;Inputs: 242 | ; HL is the numerator 243 | ; C is the denominator 244 | ;Outputs: 245 | ; A is the remainder 246 | ; B is 0 247 | ; C is not changed 248 | ; DE is not changed 249 | ; HL is the quotient 250 | 251 | HL_Div_C: 252 | 253 | ld b,24 ; 16 for non ADL mode 254 | xor a 255 | add hl,hl 256 | rla 257 | cp c 258 | jr c,$+4 259 | inc l 260 | sub c 261 | djnz $-7 262 | ret 263 | 264 | ; --------------------------------------------- 265 | ; print hex value of 0 -> 255 to screen at current TAB position 266 | 267 | printHex: 268 | push af ; store A for later 269 | and 11110000b ; get higher nibble 270 | rra 271 | rra 272 | rra 273 | rra ; move across to lower nibble 274 | add a,48 ; increase to ascii code range 0-9 275 | cp 58 ; is A less than 10? (58+) 276 | jr c, @f ; carry on if less 277 | add a, 7 ; add to get 'A' char if larger than 10 278 | @@: 279 | rst.lil $10 ; print the A char 280 | 281 | pop af ; get original A back again 282 | and 00001111b ; now just get lower nibble 283 | add a,48 ; increase to ascii code range 0-9 284 | cp 58 ; is A less than 10 (58+) 285 | jp c, @f ; carry on if less 286 | add a, 7 ; add to get 'A' char if larger than 10 287 | @@: 288 | rst.lil $10 ; print the A char 289 | 290 | ret ; head back 291 | 292 | ; --------------------------------------------- 293 | ; print zero terminated string. Arrive with HL pointer to string. 294 | 295 | printString: 296 | ld a,(hl) 297 | or a 298 | ret z 299 | RST.LIL 10h 300 | inc hl 301 | jr printString 302 | 303 | ; --------------------------------------------- 304 | ; 305 | ; TEXT and VARIABLES 306 | ; 307 | ; --------------------------------------------- 308 | 309 | msg: .asciz 17,13,"Programmable Reload Timer " 310 | msg2: .asciz " Hz.", 17, 15 311 | 312 | prt_irq_counter: .dl 0 ; what we update on screen 313 | prt_freq: .db 0 ; how often the timer runs 314 | 315 | 316 | ; --------------------------------------------- 317 | ; 318 | ; TIMER CONSTANTS 319 | ; 320 | ; --------------------------------------------- 321 | 322 | prt_reload_default: equ $FFFF ; default value of reload timer 323 | 324 | TMR1_CTL: equ $83 ; timer 1 control setup port ID 325 | TMR1_RR_L: equ $84 ; timer 1 reload counter Low byte port ID 326 | TMR1_RR_H: equ $85 ; timer 1 reload counter High byte port ID 327 | 328 | ; Timer Control Register Bit Definitions 329 | ; Only some used in this demo, but others for reference 330 | 331 | TMR_CTL: equ 80h ; base address of timer controls 332 | 333 | PRT_IRQ_0: equ %00000000 ; The timer does not reach its end-of-count value. 334 | ; This bit is reset to 0 every time the TMRx_CTL register is read. 335 | PRT_IRQ_1: equ %10000000 ; The timer reaches its end-of-count value. If IRQ_EN is set to 1, 336 | ; an interrupt signal is sent to the CPU. This bit remains 1 until 337 | ; the TMRx_CTL register is read. 338 | 339 | IRQ_EN_0: equ %00000000 ; Timer interrupt requests are disabled. 340 | IRQ_EN_1: equ %01000000 ; Timer interrupt requests are enabled. 341 | 342 | PRT_MODE_0: equ %00000000 ; The timer operates in SINGLE PASS mode. PRT_EN (bit 0) is reset to 343 | ; 0,and counting stops when the end-of-count value is reached. 344 | PRT_MODE_1: equ %00010000 ; The timer operates in CONTINUOUS mode. The timer reload value is 345 | ; written to the counter when the end-of-count value is reached. 346 | 347 | ; CLK_DIV is a 2-bit mask that sets the timer input source clock divider 348 | CLK_DIV_256: equ %00001100 ; 349 | CLK_DIV_64: equ %00001000 ; 350 | CLK_DIV_16: equ %00000100 ; 351 | CLK_DIV_4: equ %00000000 ; 352 | 353 | RST_EN_0: equ %00000000 ; The reload and restart function is disabled. 354 | RST_EN_1: equ %00000010 ; The reload and restart function is enabled. 355 | ; When a 1 is written to this bit,the values in the reload registers 356 | ; are loaded into the downcounter when the timer restarts. The 357 | ; programmer must ensure that this bit is set to 1 each time 358 | ; SINGLE-PASS mode is used. 359 | 360 | ; disable/enable the programmable reload timer 361 | PRT_EN_0: equ %00000000 ; 362 | PRT_EN_1: equ %00000001 ; 363 | 364 | ; Table 37. Timer Input Source Select Register 365 | ; Each of the 4 timers are allocated two bits of the 8-bit register 366 | ; in little-endian order,with TMR0 using bits 0 and 1,TMR1 using bits 2 and 3,etc. 367 | ; 00: System clock / CLK_DIV 368 | ; 01: RTC / CLK_DIV 369 | ; NOTE: these are the values given in the manual,but it may be a typo 370 | ; 10: GPIO port B pin 1. 371 | ; 11: GPIO port B pin 1. 372 | TMR_ISS: equ 92h ; register address 373 | 374 | ; Table 51. Real-Time Clock Control Register 375 | RTC_CTRL: equ EDh ; register address 376 | 377 | ; alarm interrupt disable/enable 378 | RTC_ALARM_0: equ %00000000 379 | RTC_ALARM_1: equ %10000000 380 | 381 | ; interrupt on alarm disable/enable 382 | RTC_INT_ENT_0: equ %00000000 383 | RTC_INT_ENT_1: equ %01000000 384 | 385 | RTC_BCD_EN_0: equ %00000000 ; RTC count and alarm registers are binary 386 | RTC_BCD_EN_1: equ %00100000 ; RTC count and alarm registers are BCD 387 | 388 | RTC_CLK_SEL_0: equ %00000000 ; RTC clock source is crystal oscillator output (32768 Hz). 389 | ; On-chip 32768 Hz oscillator is enabled. 390 | RTC_CLK_SEL_1: equ %00010000 ; RTC clock source is power line frequency input as set by FREQ_SEL. 391 | ; On-chip 32768 Hz oscillator is disabled. 392 | 393 | RTC_FREQ_SEL_0: equ %00000000 ; 60 Hz power line frequency. 394 | RTC_FREQ_SEL_1: equ %00001000 ; 50 Hz power line frequency. 395 | 396 | RTC_SLP_WAKE_0: equ %00000000 ; RTC does not generate a sleep-mode recovery reset. 397 | RTC_SLP_WAKE_1: equ %00000010 ; RTC generates a sleep-mode recovery reset. 398 | 399 | RTC_UNLOCK_0: equ %00000000 ; RTC count registers are locked to prevent Write access. 400 | ; RTC counter is enabled. 401 | RTC_UNLOCK_1: equ %00000001 ; RTC count registers are unlocked to allow Write access. 402 | ; RTC counter is disabled. 403 | 404 | 405 | -------------------------------------------------------------------------------- /joystick.asm: -------------------------------------------------------------------------------- 1 | ; AGON CONSOLE8 2 | ; Joystick Lesson 3 | ; Richard Turnnidge 2024 4 | 5 | .assume adl=1 ; big memory mode 6 | .org $40000 ; load code here 7 | 8 | jp start_here ; jump to start of code 9 | 10 | .align 64 ; MOS header 11 | .db "MOS",0,1 12 | 13 | ; --------------------------------------------- 14 | ; 15 | ; INITIAL SETUP CODE HERE 16 | ; 17 | ; --------------------------------------------- 18 | 19 | include "myMacros.inc" 20 | include "debug.asm" 21 | 22 | start_here: 23 | ; store everything as good practice 24 | 25 | push af 26 | push bc 27 | push de 28 | push ix 29 | push iy 30 | 31 | showTitle: 32 | ld hl, title_str ; data to send 33 | ld bc, end_title - title_str ; lenght of data 34 | rst.lil $18 35 | 36 | ; --------------------------------------------- 37 | ; 38 | ; MAIN LOOP 39 | ; 40 | ; --------------------------------------------- 41 | 42 | MAIN_LOOP: 43 | 44 | get_key_input: 45 | MOSCALL $08 ; get IX pointer to sysvars 46 | ld a, (ix + 05h) ; ix+5h is 'last key pressed' 47 | 48 | cp 27 ; is it ESC key? 49 | jp z, exit_here ; if so exit cleanly 50 | 51 | 52 | check_joystick: 53 | ; needed to clear flags, else it didn't seem to work 54 | ld a,0 ; set A to 0 55 | or a ; clear flags 56 | 57 | in a, (portC) ; grab current io value of port C 58 | 59 | ld b, 13 ; BC is XY of print position 60 | ld c, 5 61 | call printBin ; display value of port C in BINARY 62 | 63 | ld a,0 ; set A to 0 64 | or a ; clear flags 65 | 66 | in a, (portD) ; grab current io value of port D 67 | 68 | ld b, 13 ; BC is XY of print position 69 | ld c, 7 70 | call printBin 71 | 72 | in a, (portD) ; grab current io value of port D 73 | bit 5, a 74 | call nz, sayUp 75 | 76 | in a, (portD) ; grab current io value of port D 77 | bit 5, a 78 | call z, sayDown 79 | 80 | 81 | jp MAIN_LOOP 82 | 83 | portC: EQU $9E 84 | portD: EQU $A2 85 | 86 | sayDown: 87 | TAB_TO 1,9 88 | ld hl, downTxt 89 | ld bc,0 90 | ld a,0 91 | rst.lil $18 92 | ret 93 | 94 | sayUp: 95 | TAB_TO 1,9 96 | ld hl, upTxt 97 | ld bc,0 98 | ld a,0 99 | rst.lil $18 100 | ret 101 | 102 | downTxt: 103 | .db "Button DOWN",0 104 | 105 | upTxt: 106 | .db "Button UP ",0 107 | 108 | 109 | 110 | ; --------------------------------------------- 111 | ; 112 | ; EXIT CODE CLEANLY 113 | ; 114 | ; --------------------------------------------- 115 | 116 | exit_here: 117 | 118 | CLS 119 | ; reset all values before returning to MOS 120 | pop iy 121 | pop ix 122 | pop de 123 | pop bc 124 | pop af 125 | ld hl,0 126 | 127 | ret ; return to MOS here 128 | 129 | ; --------------------------------------------- 130 | ; 131 | ; DATA & STRINGS 132 | ; 133 | ; --------------------------------------------- 134 | 135 | title_str: 136 | .db 12 ; CLS 137 | .db 31,1,1 ; TAB to 0,0 138 | .db "Joystick Lesson z80 assembler" ; text to show 139 | 140 | .db 31,8,3 ; TAB to 0,0 141 | .db "Bits:76543210" ; text to show 142 | 143 | .db 31,13,4 ; TAB to 0,0 144 | .db "--------" ; text to show 145 | 146 | .db 31,1,5 ; TAB to 0,0 147 | .db "eZ80 Port C:" ; text to show 148 | 149 | .db 31,1,7 ; TAB to 0,0 150 | .db "eZ80 Port D:" ; text to show 151 | 152 | end_title: 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | -------------------------------------------------------------------------------- /joystick_interrupts_mode_6a.asm: -------------------------------------------------------------------------------- 1 | ; AGON & CONSOLE8 2 | ; Joystick test app - interrupt driven mode 6 dual edge interrupt 3 | ; Richard Turnnidge 2025 4 | 5 | ; --------------------------------------------- 6 | ; 7 | ; MACROS 8 | ; 9 | ; --------------------------------------------- 10 | 11 | macro MOSCALL afunc 12 | ld a, afunc 13 | rst.lil $08 14 | endmacro 15 | 16 | ; --------------------------------------------- 17 | 18 | macro TABTO x,y 19 | ld a, 31 20 | rst.lil $10 21 | ld a, x 22 | rst.lil $10 23 | ld a, y 24 | rst.lil $10 25 | endmacro 26 | 27 | ; --------------------------------------------- 28 | 29 | macro CLS 30 | ld a, 12 31 | rst.lil $10 32 | endmacro 33 | 34 | ; --------------------------------------------- 35 | 36 | macro SETCURSOR value ; to set cursor visible or not [0 or 1] 37 | push af 38 | ld a, 23 39 | rst.lil $10 40 | ld a, 1 41 | rst.lil $10 42 | ld a, value 43 | rst.lil $10 ; VDU 23,1,value [0 off, 1 on] 44 | pop af 45 | endmacro 46 | 47 | ; --------------------------------------------- 48 | ; 49 | ; CONSTANTS 50 | ; 51 | ; --------------------------------------------- 52 | 53 | ; GPIO port IDs 54 | 55 | PC_DR: equ $9E 56 | PC_DDR: equ $9F 57 | PC_ALT1: equ $A0 58 | PC_ALT2: equ $A1 59 | 60 | 61 | PD_DR: equ $A2 62 | PD_DDR: equ $A3 63 | PD_ALT1: equ $A4 64 | PD_ALT2: equ $A5 65 | 66 | joy1: equ 10101010b 67 | btn1: equ 10100000b 68 | 69 | ; --------------------------------------------- 70 | ; 71 | ; INITIALISE AGON 72 | ; 73 | ; --------------------------------------------- 74 | 75 | .assume adl=1 ; big memory mode 76 | .org $40000 ; load code here 77 | 78 | jp start_here ; jump to start of code 79 | 80 | .align 64 ; MOS header 81 | .db "MOS",0,1 82 | 83 | ; --------------------------------------------- 84 | ; 85 | ; INITIAL SETUP CODE HERE 86 | ; 87 | ; --------------------------------------------- 88 | 89 | 90 | start_here: 91 | ; store everything as good practice 92 | ; pop back when we return from code later 93 | 94 | push af 95 | push bc 96 | push de 97 | push ix 98 | push iy 99 | 100 | im 2 ; make sure running in interrupt mode 2 101 | 102 | CLS 103 | SETCURSOR 0 104 | 105 | TABTO 2,8 106 | ld hl, msg_title 107 | call printString ; print the demo title 108 | 109 | TABTO 2,15 110 | ld hl, msg_portC 111 | call printString ; print the demo title 112 | 113 | TABTO 2,19 114 | ld hl, msg_portD 115 | call printString ; print the demo title 116 | 117 | call setupIOinterruptPins 118 | 119 | call init_initerrupts 120 | 121 | ; --------------------------------------------- 122 | ; 123 | ; MAIN LOOP 124 | ; 125 | ; --------------------------------------------- 126 | 127 | MAIN_LOOP: 128 | 129 | get_key_input: 130 | MOSCALL $08 ; get IX pointer to sysvars 131 | ld a, (ix + 05h) ; ix+5h is 'last key pressed' 132 | 133 | cp 27 ; is it ESC key? 134 | jp z, exit_here ; if so exit cleanly 135 | 136 | jp MAIN_LOOP 137 | 138 | ; --------------------------------------------- 139 | ; 140 | ; EXIT CODE CLEANLY 141 | ; 142 | ; --------------------------------------------- 143 | 144 | exit_here: 145 | 146 | call resetPins 147 | CLS 148 | SETCURSOR 1 149 | ; reset all values before returning to MOS 150 | pop iy 151 | pop ix 152 | pop de 153 | pop bc 154 | pop af 155 | ld hl,0 156 | 157 | ret ; return to MOS here 158 | 159 | ; --------------------------------------------- 160 | ; 161 | ; IO PORT INIT 162 | ; 163 | ; --------------------------------------------- 164 | 165 | setupIOinterruptPins: 166 | 167 | di 168 | 169 | ld a, joy1 170 | out0 (PC_DR), a ; set DR of all port C to 0 171 | 172 | ld a, $00 173 | out0 (PC_DDR), a ; set DDR of all port C to 255 174 | 175 | ld a, $00 176 | out0 (PC_ALT1), a ; set ALT1 of all port C to 255 177 | 178 | ld a, joy1 179 | out0 (PC_ALT2), a ; set ALT2 of all port C to 255 180 | 181 | ; port D - only need bits 4-7 182 | ; as port is shared with other Agon tasks, we need to grab current 183 | ; value so we don't upset bits 0-3 184 | 185 | in0 a, (PD_DR) 186 | or 10100000b 187 | out0 ($a2), a ; set DR of port D bits 4-7 to 255 188 | 189 | in0 a, (PD_DDR) 190 | and 00001111b 191 | out0 (PD_DDR), a ; set DDR of port D bits 4-7 to 0 192 | 193 | in0 a, (PD_ALT1) 194 | and 00001111b 195 | out0 (PD_ALT1), a ; set ALT1 of port D bits 4-7 to 0 196 | 197 | in0 a, (PD_ALT2) 198 | or 10100000b 199 | out0 (PD_ALT2), a ; set ALT2 of port D bits 4-7 to 255 200 | 201 | ei 202 | 203 | ret 204 | 205 | ; --------------------------------------------- 206 | 207 | resetPins: 208 | 209 | di 210 | 211 | ; clear port C 212 | ld a, $00 213 | out0 (PC_DR), a ; set DR of all port C to 0 default - 158 214 | 215 | ld a, $00 216 | out0 (PC_DDR), a ; set DDR of all port C to 0 - 159 217 | 218 | ld a, $00 219 | out0 (PC_ALT1), a ; set ALT1 of all port C to 0 - 160 220 | 221 | ld a, $00 222 | out0 (PC_ALT2), a ; set ALT2 of all port C to 0 - 161 223 | 224 | 225 | ; clear port D 226 | in0 a, (PD_DR) 227 | and 00001111b 228 | out0 ($a2), a ; set DR of port D bits 4-7 to 0 229 | 230 | in0 a, (PD_DDR) 231 | and 00001111b 232 | out0 ($a3), a ; set DDR of port D bits 4-7 to 0 233 | 234 | in0 a, (PD_ALT1) 235 | and 00001111b 236 | out0 ($a4), a ; set ALT1 of port D bits 4-7 to 0 237 | 238 | in0 a, (PD_ALT2) 239 | and 00001111b 240 | out0 ($a5), a ; set ALT2 of port D bits 4-7 to 0 241 | 242 | ei 243 | 244 | ret 245 | 246 | ; --------------------------------------------- 247 | ; 248 | ; INTERRUPT ROUTINES 249 | ; 250 | ; --------------------------------------------- 251 | 252 | init_initerrupts: 253 | 254 | ld hl, isr_up_event ; address of service routine to call 255 | ld e, $42 ; interrupt number (PC1) 256 | MOSCALL $14 ; mos_api_setintvector 257 | 258 | ld hl, isr_down_event ; address of service routine to call 259 | ld e, $46 ; interrupt number (PC3) 260 | MOSCALL $14 ; mos_api_setintvector 261 | 262 | ld hl, isr_left_event ; address of service routine to call 263 | ld e, $4A ; interrupt number (PC5) 264 | MOSCALL $14 ; mos_api_setintvector 265 | 266 | ld hl, isr_right_event ; address of service routine to call 267 | ld e, $4E ; interrupt number (PC7) 268 | MOSCALL $14 ; mos_api_setintvector 269 | 270 | ld hl, isr_fire_event ; address of service routine to call 271 | ld e, $5A ; interrupt number (PD5) 272 | MOSCALL $14 ; mos_api_setintvector 273 | 274 | ld hl, isr_fire2_event ; address of service routine to call 275 | ld e, $5E ; interrupt number (PD7) 276 | MOSCALL $14 ; mos_api_setintvector 277 | 278 | ret 279 | 280 | ; --------------------------------------------- 281 | 282 | isr_up_event: 283 | 284 | di 285 | push hl 286 | push af 287 | push bc 288 | 289 | call showPins 290 | 291 | in0 a, (PC_DR) 292 | ld b, a 293 | bit 1, b 294 | jr nz, @nope 295 | 296 | TABTO 2,10 297 | ld hl, msg_up_hit 298 | call printString 299 | jr @end 300 | 301 | @nope: 302 | TABTO 2,10 303 | ld hl, msg_up_release 304 | call printString 305 | 306 | @end: 307 | 308 | in0 a, (PC_DR) 309 | or joy1 310 | out0 (PC_DR), a ; set DR of all port C to 255 default - 158 311 | 312 | pop bc 313 | pop af 314 | pop hl 315 | 316 | ei 317 | reti.l ; return from interrupt routine 318 | 319 | ; --------------------------------------------- 320 | 321 | isr_down_event: 322 | 323 | di 324 | push hl 325 | push af 326 | push bc 327 | 328 | call showPins 329 | 330 | in0 a, (PC_DR) 331 | ld b,a 332 | 333 | bit 3, b 334 | jr nz, @nope 335 | 336 | TABTO 2,10 337 | ld hl, msg_down_hit 338 | call printString 339 | jr @end 340 | 341 | @nope: 342 | TABTO 2,10 343 | ld hl, msg_down_release 344 | call printString 345 | 346 | @end: 347 | 348 | in0 a, (PC_DR) 349 | or joy1 350 | out0 (PC_DR), a ; set DR of all port C to 255 default - 158 351 | 352 | pop bc 353 | pop af 354 | pop hl 355 | ei 356 | reti.l ; return from interrupt routine 357 | 358 | ; --------------------------------------------- 359 | 360 | isr_left_event: 361 | 362 | di 363 | push hl 364 | push af 365 | push bc 366 | 367 | call showPins 368 | 369 | in0 a, (PC_DR) 370 | ld b,a 371 | 372 | bit 5, b 373 | jr nz, @nope 374 | 375 | TABTO 2,10 376 | ld hl, msg_left_hit 377 | call printString 378 | jr @end 379 | 380 | @nope: 381 | TABTO 2,10 382 | ld hl, msg_left_release 383 | call printString 384 | 385 | @end: 386 | 387 | in0 a, (PC_DR) 388 | or joy1 389 | out0 (PC_DR), a ; set DR of all port C to 255 default - 158 390 | 391 | pop bc 392 | pop af 393 | pop hl 394 | ei 395 | reti.l ; return from interrupt routine 396 | 397 | ; --------------------------------------------- 398 | 399 | isr_right_event: 400 | 401 | di 402 | push hl 403 | push af 404 | push bc 405 | 406 | call showPins 407 | 408 | in0 a, (PC_DR) 409 | ld b,a 410 | 411 | bit 7, b 412 | jr nz, @nope 413 | 414 | TABTO 2,10 415 | ld hl, msg_right_hit 416 | call printString 417 | jr @end 418 | 419 | @nope: 420 | TABTO 2,10 421 | ld hl, msg_right_release 422 | call printString 423 | 424 | @end: 425 | 426 | in0 a, (PC_DR) 427 | or joy1 428 | out0 (PC_DR), a ; set DR of all port C to 255 default - 158 429 | 430 | pop bc 431 | pop af 432 | pop hl 433 | ei 434 | reti.l ; return from interrupt routine 435 | 436 | ; --------------------------------------------- 437 | ; bit 5 438 | isr_fire_event: 439 | 440 | di 441 | push hl 442 | push af 443 | 444 | push bc 445 | 446 | call showPins 447 | 448 | in0 a, (PD_DR) 449 | ld b,a 450 | 451 | bit 5, b 452 | jr nz, @nope 453 | 454 | TABTO 2,10 455 | ld hl, msg_fire_hit 456 | call printString 457 | jr @end 458 | 459 | @nope: 460 | TABTO 2,10 461 | ld hl, msg_fire_release 462 | call printString 463 | 464 | @end: 465 | 466 | in0 a, (PD_DR) 467 | or btn1 468 | out0 (PD_DR), a ; set DR of all port C to 255 default - 158 469 | 470 | pop bc 471 | pop af 472 | pop hl 473 | ei 474 | reti.l ; return from interrupt routine 475 | 476 | ; --------------------------------------------- 477 | ; bit 7 478 | isr_fire2_event: 479 | 480 | di 481 | push hl 482 | push af 483 | push bc 484 | 485 | call showPins 486 | 487 | in0 a, (PD_DR) 488 | ld b,a 489 | 490 | bit 7, b 491 | jr nz, @nope 492 | 493 | TABTO 2,10 494 | ld hl, msg_fire2_hit 495 | call printString 496 | jr @end 497 | 498 | @nope: 499 | TABTO 2,10 500 | ld hl, msg_fire2_release 501 | call printString 502 | 503 | @end: 504 | 505 | in0 a, (PD_DR) 506 | or btn1 507 | out0 (PD_DR), a ; set DR of all port C to 255 default - 158 508 | 509 | pop bc 510 | pop af 511 | pop hl 512 | ei 513 | reti.l ; return from interrupt routine 514 | 515 | ; --------------------------------------------- 516 | 517 | 518 | showPins: 519 | 520 | TABTO 2,16 521 | 522 | in0 a, (PC_DR) 523 | call printBin 524 | 525 | TABTO 2,20 526 | 527 | in0 a, (PD_DR) 528 | call printBin 529 | 530 | ret 531 | 532 | ; --------------------------------------------- 533 | ; 534 | ; OTHER ROUTINES 535 | ; 536 | ; --------------------------------------------- 537 | 538 | ; take A as number and print out as binary 539 | ; will destroy HL, BC 540 | 541 | printBin: 542 | push bc ; store BC for later 543 | ld b, 8 ; number of bits to do 544 | ld hl, binString 545 | @rpt: 546 | ld (hl), 48 ; set our byte to '0' as default 547 | 548 | bit 7, a ; check if bit is set 549 | jr z, @nxt ; if not, move on to next bit 550 | ld (hl), 49 ; set our byte to '1' 551 | @nxt: 552 | inc hl ; set next position in output 'binString' 553 | rla ; rotate byte A so that bit 7 is next bit 554 | djnz @rpt ; loop round until done 8 times 555 | 556 | ld hl, binString ; HL is address of the 'binary' string of 8 bytes 557 | call printString ; print the string at HL 558 | pop bc ; restore BC 559 | ret 560 | 561 | binString: .asciz "00000000" ; 8 chars plus a zero 562 | 563 | ; --------------------------------- 564 | ; print zero terminated string 565 | ; Any char apart from zero will be sent to VDP 566 | 567 | printString: 568 | ld a,(hl) ; put char byte into A 569 | or a ; check if zero 570 | ret z ; return if it is, as we are at the end of the zero terminated string 571 | rst.lil $10 ; send byte A to VDP 572 | inc hl ; move to next byte in the string 573 | jr printString ; loop around and test next byte 574 | 575 | 576 | ; --------------------------------------------- 577 | ; 578 | ; TEXT DATA 579 | ; 580 | ; --------------------------------------------- 581 | 582 | msg_title: .asciz "Joystick Interrupt Handler " 583 | 584 | msg_portC: .asciz "Port C " 585 | msg_portD: .asciz "Port D " 586 | 587 | msg_up_hit: .asciz "UP Pressed " 588 | msg_up_release: .asciz "UP Released " 589 | 590 | msg_down_hit: .asciz "DOWN Pressed " 591 | msg_down_release: .asciz "DOWN Released " 592 | 593 | msg_left_hit: .asciz "LEFT Pressed " 594 | msg_left_release: .asciz "LEFT Released " 595 | 596 | msg_right_hit: .asciz "RIGHT Pressed " 597 | msg_right_release: .asciz "RIGHT Released " 598 | 599 | msg_fire_hit: .asciz "FIRE Pressed " 600 | msg_fire_release: .asciz "FIRE Released " 601 | 602 | msg_fire2_hit: .asciz "BLASTER Pressed " 603 | msg_fire2_release: .asciz "BLASTER Released " 604 | 605 | 606 | ; --------------------------------------------- 607 | ; 608 | ; END 609 | ; 610 | ; --------------------------------------------- 611 | -------------------------------------------------------------------------------- /joystick_ports.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/richardturnnidge/lessons/3cb69781f37a25d3ff7da6e31cd59a7ee4dc19b5/joystick_ports.png -------------------------------------------------------------------------------- /key_matrix.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/richardturnnidge/lessons/3cb69781f37a25d3ff7da6e31cd59a7ee4dc19b5/key_matrix.pdf -------------------------------------------------------------------------------- /keyboard_interrupt.asm: -------------------------------------------------------------------------------- 1 | ; AGON LIGHT 2 | ; Keyboard Interrupt Example 3 | ; Richard Turnnidge 2025 4 | 5 | ; --------------------------------------------- 6 | ; 7 | ; MACROS 8 | ; 9 | ; --------------------------------------------- 10 | 11 | macro MOSCALL function 12 | ld a, function 13 | rst.lil $08 14 | endmacro 15 | 16 | ; --------------------------------------------- 17 | 18 | macro TABTO x, y 19 | ld a, 31 20 | rst.lil $10 21 | ld a, x 22 | rst.lil $10 23 | ld a, y 24 | rst.lil $10 25 | endmacro 26 | 27 | ; --------------------------------------------- 28 | 29 | macro CLS 30 | ld a, 12 31 | rst.lil $10 32 | endmacro 33 | 34 | ; --------------------------------------------- 35 | 36 | macro MODE mode 37 | ld a, 22 38 | rst.lil $10 39 | ld a, mode 40 | rst.lil $10 41 | endmacro 42 | 43 | ; --------------------------------------------- 44 | 45 | macro SETCOLOUR col 46 | ld a, 17 47 | rst.lil $10 48 | ld a, col 49 | rst.lil $10 50 | endmacro 51 | 52 | ; --------------------------------------------- 53 | 54 | macro SETCURSOR value ; to set cursor visible or not [0 or 1] 55 | push af 56 | ld a, 23 57 | rst.lil $10 58 | ld a, 1 59 | rst.lil $10 60 | ld a, value 61 | rst.lil $10 ; VDU 23,1,value [0 off, 1 on] 62 | pop af 63 | endmacro 64 | 65 | ; --------------------------------------------- 66 | ; 67 | ; CONSTANTS 68 | ; 69 | ; --------------------------------------------- 70 | 71 | colour: equ 17 72 | white: equ 15 73 | grey: equ 7 74 | green: equ 10 75 | red: equ 1 76 | 77 | ; --------------------------------------------- 78 | ; 79 | ; GET READY 80 | ; 81 | ; --------------------------------------------- 82 | 83 | .assume adl=1 ; big memory mode 84 | .org $40000 ; load code here 85 | 86 | jp start_here ; jump to start of code 87 | 88 | .align 64 ; MOS header 89 | .db "MOS",0,1 90 | 91 | ; --------------------------------------------- 92 | ; 93 | ; START 94 | ; 95 | ; --------------------------------------------- 96 | 97 | start_here: 98 | 99 | push af 100 | push bc 101 | push de 102 | push ix ; store everything as good practice 103 | push iy ; pop back when we return from code later 104 | 105 | MODE 8 ; set display to mode 8 106 | CLS ; clear screen 107 | SETCURSOR 0 ; hide cursor 108 | SETCOLOUR white ; reset text colour to white 109 | 110 | ld hl, title_str ; data to send 111 | ld bc, end_title - title_str ; length of data 112 | rst.lil $18 ; print screen info text 113 | 114 | call setup_kb_handler ; initiate keyboard intercepter 115 | 116 | ; --------------------------------------------- 117 | ; 118 | ; MAIN LOOP 119 | ; 120 | ; --------------------------------------------- 121 | 122 | MAIN_LOOP: 123 | 124 | get_key_input: 125 | MOSCALL $08 ; get IX pointer to sysvars 126 | ld a, (ix + 05h) ; ix+5h is 'last key pressed' 127 | 128 | cp 27 ; is it ESC key? 129 | jp z, exit_here ; if so exit cleanly 130 | 131 | jp MAIN_LOOP 132 | 133 | ; --------------------------------------------- 134 | ; 135 | ; EXIT CODE CLEANLY 136 | ; 137 | ; --------------------------------------------- 138 | 139 | exit_here: 140 | 141 | call reset_kb_handler ; clear event handler 142 | 143 | SETCURSOR 1 ; make cursor visible again 144 | SETCOLOUR white ; reset text colour to white 145 | 146 | CLS ; clear screen 147 | 148 | pop iy 149 | pop ix 150 | pop de 151 | pop bc 152 | pop af 153 | ld hl,0 ; reset all values before returning to MOS 154 | 155 | ret ; return to MOS here 156 | 157 | ; --------------------------------------------- 158 | 159 | setup_kb_handler: 160 | ld hl, on_keyboard_event ; address of the routine to call 161 | ld c, 0 ; address length. 0 = 24bit, 1 = 16 bit 162 | moscall $1D ; mos_setkbvector 163 | ret 164 | 165 | ; --------------------------------------------- 166 | 167 | reset_kb_handler: 168 | ld hl, 0 ; nil address = clear event 169 | ld c, 0 ; address length. 0 = 24bit, 1 = 16 bit 170 | moscall $1D ; mos_setkbvector 171 | ret 172 | 173 | ; --------------------------------------------- 174 | 175 | ; with each event, DE points to a structure of keyboard info 176 | ; DE + 0 ; ascii code 177 | ; DE + 1 ; modifier keys 178 | ; DE + 2 ; fabgl vkey code 179 | ; DE + 3 ; is key down? 180 | 181 | on_keyboard_event: 182 | push bc 183 | push hl 184 | push ix ; backup any registers we might use 185 | 186 | push de 187 | pop ix ; put DE into IX for easier reading 188 | 189 | SETCOLOUR green ; reset text colour to white 190 | 191 | TABTO 17,6 192 | ld a, (ix+0) ; ascii code 193 | call debugHex 194 | 195 | TABTO 17, 8 196 | ld a, (ix+1) ; modifier keys 197 | call debugHex 198 | 199 | TABTO 17, 10 200 | ld a, (ix+2) ; fabgl vkey code 201 | call debugHex 202 | 203 | TABTO 17, 12 204 | ld a, (ix+3) ; is key down? 205 | call debugHex 206 | 207 | SETCOLOUR white ; reset text colour to white 208 | 209 | pop ix 210 | pop hl 211 | pop bc ; restore registers 212 | 213 | ret 214 | 215 | ; --------------------------------------------- 216 | ; 217 | ; TEXT AND DATA 218 | ; 219 | ; --------------------------------------------- 220 | 221 | title_str: 222 | 223 | .db 31, 0,0,"Keyboard Interrupt example" ; text to show 224 | .db 31, 0,2,"VDP packet data" ; text to show 225 | 226 | .db 31, 0,6, colour, grey, "ASCII code:" ; text to show 227 | .db 31, 0,8, "Modifier code:" ; text to show 228 | .db 31, 0,10, "FabGL vKey code:" ; text to show 229 | .db 31, 0,12, "Up (0) Down (1):" ; text to show 230 | 231 | .db 31, 0,16, "Press ", colour, red, "ESC", colour, grey," to exit" ; text to show 232 | 233 | end_title: 234 | 235 | ; --------------------------------------------- 236 | ; 237 | ; OTHER ROUTINES 238 | ; 239 | ; --------------------------------------------- 240 | 241 | ; debug A to screen as HEX byte pair at current position 242 | 243 | debugHex: 244 | push af 245 | push af ; store A twice 246 | 247 | push af 248 | ld a, '$' 249 | rst.lil $10 ; print the $ char 250 | pop af 251 | 252 | and 11110000b ; get higher nibble 253 | rra 254 | rra 255 | rra 256 | rra ; move across to lower nibble 257 | add a,48 ; increase to ascii code range 0-9 258 | cp 58 ; is A less than 10? (58+) 259 | jr c, @f ; carry on if less 260 | add a, 7 ; add to get 'A' char if larger than 10 261 | @@: 262 | rst.lil $10 ; print the A char 263 | 264 | pop af ; get A back 265 | and 00001111b ; now just get lower nibble 266 | add a,48 ; increase to ascii code range 0-9 267 | cp 58 ; is A less than 10 (58+) 268 | jp c, @f ; carry on if less 269 | add a, 7 ; add to get 'A' char if larger than 10 270 | @@: 271 | rst.lil $10 ; print the A char 272 | 273 | pop af ; get initial A back in case needed 274 | ret ; head back 275 | 276 | 277 | 278 | ; --------------------------------------------- 279 | ; 280 | ; END 281 | ; 282 | ; --------------------------------------------- 283 | -------------------------------------------------------------------------------- /mouse.asm: -------------------------------------------------------------------------------- 1 | ; extra MACRO files need to go here 2 | include "myMacros.inc" 3 | 4 | .assume adl=1 ; ez80 ADL memory mode 5 | .org $40000 ; load code here 6 | 7 | jp start_here ; jump to start of code 8 | 9 | .align 64 ; MOS header 10 | .db "MOS",0,1 11 | 12 | start_here: 13 | 14 | push af ; store all the registers 15 | push bc 16 | push de 17 | push ix 18 | push iy 19 | 20 | ; ------------------ 21 | ; This is our actual code 22 | 23 | ; prepare the screen 24 | 25 | SET_MODE 8 ; mode 8 is 320x240 pixels, 64 colours 26 | 27 | ; enable mouse command 28 | ld a, 23 29 | rst.lil $10 30 | ld a, 0 31 | rst.lil $10 32 | ld a, 89h 33 | rst.lil $10 34 | ld a, 0 ; 0=enable, 1=disable 35 | rst.lil $10 36 | 37 | ; set mouse icon style 38 | ld a, 23 39 | rst.lil $10 40 | ld a, 0 41 | rst.lil $10 42 | ld a, 89h 43 | rst.lil $10 44 | ld a, 3 ; setCursor 45 | rst.lil $10 46 | ld a, 5 ; fab-gl style number, or my own defined ones 47 | rst.lil $10 48 | ld a, 0 ; 0 as 16 bit number 49 | rst.lil $10 50 | 51 | ; Sending an initialising VDU byte stream 52 | 53 | ld hl, VDUdata ; address of string to use 54 | ld bc, endVDUdata - VDUdata ; length of string 55 | rst.lil $18 ; Call the MOS API to send data to VDP 56 | 57 | ; ------------------ 58 | 59 | MOUSE_IS_UP_LOOP: ; loop here until we hit ESC key 60 | 61 | MOSCALL $1E ; load IX with keymap address 62 | ld a, (ix + $0E) 63 | bit 0, a ; index $0E, bit 0 is for ESC key in matrix 64 | jp nz, EXIT_HERE ; if pressed, ESC key to exit 65 | 66 | 67 | MOSCALL $08 ; get IX pointer to sysvars 68 | ld a, (ix + $2D) ; + $2D is mouse buttons status 69 | and 00000001b ; bit 0 is left mouse button 70 | cp 1 ; check if pressed 71 | jp z, DO_MOUSE_DOWN ; if so, then jump to mouse down routines 72 | 73 | jp MOUSE_IS_UP_LOOP 74 | 75 | ; --------------- 76 | 77 | DO_MOUSE_DOWN: 78 | ; set start position 79 | ld a, (ix + $29) ; mouse x position 80 | ld (mouse_start_x), a 81 | ld a, (ix + $2A) ; mouse x position 82 | ld (mouse_start_x + 1), a 83 | 84 | ld a, (ix + $2B) ; mouse y position 85 | ld (mouse_start_y), a 86 | ld a, (ix + $2C) ; mouse y position 87 | ld (mouse_start_y + 1), a 88 | 89 | call plotStart 90 | 91 | jp MOUSE_STILLDOWN_LOOP 92 | 93 | ; ------------------ 94 | 95 | plotStart: 96 | ld hl, plot ; address of string to use 97 | ld bc, endPlot - plot ; length of string 98 | rst.lil $18 ; Call the MOS API to send data to VDP 99 | ret 100 | 101 | plot: 102 | .db 25, 69 ; PLOT point at... 103 | mouse_start_x: .dw 0 ; x 104 | mouse_start_y: .dw 0 ; y 105 | 106 | endPlot: 107 | 108 | ; ------------------ 109 | 110 | MOUSE_STILLDOWN_LOOP: 111 | 112 | MOSCALL $1E ; load IX with keymap address 113 | ld a, (ix + $0E) 114 | bit 0, a ; index $0E, bit 0 is for ESC key in matrix 115 | jp nz, EXIT_HERE ; if pressed, ESC key to exit 116 | 117 | MOSCALL $08 ; get IX pointer to sysvars 118 | ld a, (ix + $2D) ; +$2D is mouse buttons status 119 | and 00000001b 120 | cp 0 121 | jp z, MOUSE_IS_UP_LOOP 122 | 123 | ld a, (ix + $29) ; mouse x position 124 | ld (mouse_pos_x), a 125 | ld a, (ix + $2A) ; mouse x position 126 | ld (mouse_pos_x + 1), a 127 | 128 | ld a, (ix + $2B) ; mouse y position 129 | ld (mouse_pos_y), a 130 | ld a, (ix + $2C) ; mouse y position 131 | ld (mouse_pos_y + 1), a 132 | 133 | call drawLine 134 | 135 | jp MOUSE_STILLDOWN_LOOP 136 | 137 | ; ------------------ 138 | 139 | drawLine: 140 | ld hl, line ; address of string to use 141 | ld bc, endLine - line ; length of string 142 | rst.lil $18 ; Call the MOS API to send data to VDP 143 | ret 144 | 145 | line: 146 | .db 25, 5 ; PLOT line from last position to... 147 | mouse_pos_x: .dw 0 ; x 148 | mouse_pos_y: .dw 0 ; y 149 | 150 | endLine: 151 | 152 | 153 | ; ------------------ 154 | ; This is where we exit the program 155 | 156 | EXIT_HERE: 157 | 158 | CLS 159 | 160 | ; disable mouse command 161 | ld a, 23 162 | rst.lil $10 163 | ld a, 0 164 | rst.lil $10 165 | ld a, 89h 166 | rst.lil $10 167 | ld a, 1 ; 0=enable, 1=disable 168 | rst.lil $10 169 | 170 | pop iy ; Pop all registers back from the stack 171 | pop ix 172 | pop de 173 | pop bc 174 | pop af 175 | ld hl,0 ; Load the MOS API return code (0) for no errors. 176 | ret ; Return to MOS 177 | 178 | ; ------------------ 179 | ; This is the data we send to VDP 180 | 181 | VDUdata: 182 | .db 23, 0, 192, 0 ; set to non-scaled graphics 183 | 184 | endVDUdata: 185 | 186 | ; ------------------ 187 | 188 | ; Cursor styles 189 | 190 | ; 0 CursorPointerAmigaLike 191 | ; 11x11 Amiga like colored mouse pointer 192 | 193 | ; 1 CursorPointerSimpleReduced 194 | ; 10x15 mouse pointer 195 | 196 | ; 2 CursorPointerSimple 197 | ; 11x19 mouse pointer 198 | 199 | ; 3 CursorPointerShadowed 200 | ; 11x19 shadowed mouse pointer 201 | 202 | ; 4 CursorPointer 203 | ; 12x17 mouse pointer 204 | 205 | ; 5 CursorPen 206 | ; 16x16 pen 207 | 208 | ; 6 CursorCross1 209 | ; 9x9 cross 210 | 211 | ; 7 CursorCross2 212 | ; 11x11 cross 213 | 214 | ; 8 CursorPoint 215 | ; 5x5 point 216 | 217 | ; 9 CursorLeftArrow 218 | ; 11x11 left arrow 219 | 220 | ; 10 CursorRightArrow 221 | ; 11x11 right arrow 222 | 223 | ; 11 CursorDownArrow 224 | ; 11x11 down arrow 225 | 226 | ; 12 CursorUpArrow 227 | ; 11x11 up arrow 228 | 229 | ; 13 CursorMove 230 | ; 19x19 move 231 | 232 | ; 14 CursorResize1 233 | ; 12x12 resize orientation 1 234 | 235 | ; 15 CursorResize2 236 | ; 12x12 resize orientation 2 237 | 238 | ; 16 CursorResize3 239 | ; 11x17 resize orientation 3 240 | 241 | ; 17 CursorResize4 242 | ; 17x11 resize orientation 4 243 | 244 | ; 18 CursorTextInput 245 | ; 7x15 text input 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | -------------------------------------------------------------------------------- /myMacros.inc: -------------------------------------------------------------------------------- 1 | ; -------------------------------- 2 | ; 3 | ; SOME USEFUL MACROS 4 | ; 5 | ; -------------------------------- 6 | 7 | macro CLS 8 | ld a, 12 9 | rst.lil $10 10 | endmacro 11 | 12 | macro SET_COLOUR value 13 | ld a, 17 ; set text colour 14 | rst.lil $10 15 | ld a, value ; colour to use 16 | rst.lil $10 17 | endmacro 18 | 19 | macro SET_BG_COLOUR value 20 | ld a, 17 ; set text colour 21 | rst.lil $10 22 | ld a, value ; colour to use 23 | add a, 128 24 | rst.lil $10 25 | endmacro 26 | 27 | macro TAB_TO x,y 28 | ld a, 31 ; move to... 29 | rst.lil $10 30 | ld a, x ; X position 31 | rst.lil $10 32 | ld a, y ; Y position 33 | rst.lil $10 34 | endmacro 35 | 36 | macro SET_MODE mode 37 | ld a, 22 ; set mode... 38 | rst.lil $10 39 | ld a, mode ; to mode 40 | rst.lil $10 41 | endmacro 42 | 43 | macro MOSCALL arg1 44 | ld a, arg1 45 | rst.lil $08 46 | endmacro 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /plot.asm: -------------------------------------------------------------------------------- 1 | ; extra MACRO files need to go here 2 | include "myMacros.inc" 3 | 4 | .assume adl=1 ; ez80 ADL memory mode 5 | .org $40000 ; load code here 6 | 7 | jp start_here ; jump to start of code 8 | 9 | .align 64 ; MOS header 10 | .db "MOS",0,1 11 | 12 | start_here: 13 | 14 | push af ; store all the registers 15 | push bc 16 | push de 17 | push ix 18 | push iy 19 | 20 | ; ------------------ 21 | ; This is our actual code 22 | 23 | ; prepare the screen 24 | 25 | SET_MODE 8 ; mode 8 is 640x480 pixels, 64 colours 26 | 27 | 28 | ; Sending a VDU byte stream 29 | 30 | ld hl, VDUdata ; address of string to use 31 | ld bc, endVDUdata - VDUdata ; length of string 32 | rst.lil $18 ; Call the MOS API to send data to VDP 33 | 34 | ld a, $08 ; code to send to MOS 35 | rst.lil $08 ; get IX pointer to System Variables 36 | 37 | WAIT_HERE: ; loop here until we hit ESC key 38 | ld a, (ix + $05) ; get ASCII code of key pressed 39 | cp 27 ; check if 27 (ascii code for ESC) 40 | jp z, EXIT_HERE ; if pressed, jump to exit 41 | 42 | jr WAIT_HERE 43 | 44 | ; ------------------ 45 | ; This is where we exit the program 46 | 47 | EXIT_HERE: 48 | 49 | CLS 50 | pop iy ; Pop all registers back from the stack 51 | pop ix 52 | pop de 53 | pop bc 54 | pop af 55 | ld hl,0 ; Load the MOS API return code (0) for no errors. 56 | ret ; Return to MOS 57 | 58 | ; ------------------ 59 | ; This is the data we send to VDP 60 | 61 | VDUdata: 62 | .db 23, 0, 192, 0 ; set to non-scaled graphics 63 | 64 | ; FOR A SINGLE PIXEL PLOT 65 | 66 | .db 18, 0, bright_red ; set graphics colour: mode (0), colour 67 | 68 | .db 25, 69 ; PLOT: mode (69 is a point in current colour), 69 | .dw 200,80 ; X; Y; 70 | 71 | ; FOR A LINE 72 | 73 | .db 18, 0, bright_magenta ; set graphics colour: mode (0), colour 74 | 75 | .db 25, 69 ; PLOT: mode (69 is a point in current colour), 76 | .dw 300, 60 ; X; Y; 77 | 78 | .db 25, 13 ; PLOT: mode (13 is a line), 79 | .dw 250,130 ; X; Y; 80 | 81 | ; FOR A RECTANGLE 82 | 83 | .db 18, 0, green ; set graphics colour: mode (0), colour 84 | 85 | .db 25, 69 ; PLOT: mode (69 is a point in current colour), 86 | .dw 10,120 ; X; Y; 87 | 88 | .db 25, 101 ; PLOT: mode (101 is a filled rectangle), 89 | .dw 100,180 ; X; Y; 90 | 91 | 92 | ; FOR A CIRCLE 93 | 94 | .db 18, 0, bright_yellow ; set graphics colour: mode (0), colour 95 | 96 | .db 25, 68 ; PLOT: mode (69 is a MOVE TO but don't plot point), 97 | .dw 180,140 ; X; Y; 98 | 99 | .db 25, 149 ; PLOT: mode (149 is an outlined circle), 100 | .dw 200,160 ; X; Y; 101 | 102 | ; FOR A FILLED TRIANGLE 103 | 104 | .db 18, 0, blue ; set graphics colour: mode (0), colour 105 | 106 | .db 25, 69 ; PLOT: mode (69 is a point in current colour), 107 | .dw 10,10 ; X; Y; 108 | 109 | .db 25, 69 ; PLOT: mode (69 is a point in current colour), 110 | .dw 50, 100 ; X; Y; 111 | 112 | .db 25, 85 ; PLOT: mode (85 is a filled triangle), 113 | .dw 200,20 ; X; Y; 114 | 115 | endVDUdata: 116 | 117 | ; ------------------ 118 | ; colour data 119 | 120 | bright_red: equ 9 121 | green: equ 2 122 | bright_yellow: equ 11 123 | bright_magenta: equ 13 124 | blue: equ 4 125 | white: equ 7 126 | black: equ 0 127 | bright_white: equ 15 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | -------------------------------------------------------------------------------- /random.asm: -------------------------------------------------------------------------------- 1 | ; extra MACRO files need to go here 2 | include "myMacros.inc" 3 | 4 | .assume adl=1 ; ez80 ADL memory mode 5 | .org $40000 ; load code here 6 | 7 | jp start_here ; jump to start of code 8 | 9 | .align 64 ; MOS header 10 | .db "MOS",0,1 11 | 12 | ; extra code files need to go here, or later 13 | include "debug.asm" 14 | 15 | start_here: 16 | 17 | push af ; store all the registers 18 | push bc 19 | push de 20 | push ix 21 | push iy 22 | 23 | ; ------------------ 24 | ; This is our actual code 25 | 26 | ld hl, VDUdata ; address of string to use 27 | ld bc, endVDUdata - VDUdata ; length of string 28 | rst.lil $18 ; display message 29 | 30 | 31 | LOOP_HERE: ; loop here until we hit ESC or SPACE key 32 | 33 | MOSCALL $1E ; load IX with keymap address 34 | ld a, (ix + $0E) 35 | bit 0, a ; index $0E, bit 0 is for ESC key in matrix 36 | jp nz, EXIT_HERE ; if pressed, ESC key to exit 37 | 38 | ld a, (ix + $0C) 39 | bit 2, a ; index $0C bit 2 is for SPACE key in matrix 40 | call nz, DO_RANDOM ; if pressed, SPACE key to exit 41 | 42 | jr LOOP_HERE 43 | 44 | ; ------------------ 45 | 46 | DO_RANDOM: 47 | ld a, (ix + $0C) 48 | bit 2, a ; index $0C bit 2 is for SPACE key in matrix 49 | jp nz, DO_RANDOM ; if pressed, SPACE key to exit 50 | 51 | call PRINT_RANDOM1 52 | call PRINT_RANDOM2 53 | 54 | ret 55 | 56 | PRINT_RANDOM1: 57 | MOSCALL $08 ; load IX with sysvars address 58 | ld a, (ix + $0) ; get first byte of clock counter 59 | 60 | ld b, 0 61 | ld c, 4 62 | call printHexA ; display it 63 | 64 | ret 65 | 66 | PRINT_RANDOM2: 67 | ; LD A, 0 is created here with two bytes, $3E $00 68 | .db $3E ; start of LD A, number 69 | randSeed: 70 | .db $00 ; 2nd part of LD A, number 71 | push bc 72 | 73 | ld c,a 74 | add a,a 75 | add a,c 76 | add a,a 77 | add a,a 78 | add a,c 79 | add a,83 80 | ld (randSeed),a 81 | pop bc 82 | 83 | ld b, 0 84 | ld c, 6 85 | call printHexA 86 | 87 | ret 88 | 89 | ; ------------------ 90 | ; This is the data we send to VDP 91 | 92 | VDUdata: 93 | .db 22,8 ; set screen to MODE 8, this will also clear screen 94 | .db 31, 4, 4 ; TAB to 10,20 95 | .db "Clock based randomness" ; print this text 96 | 97 | .db 31, 4, 6 ; TAB to 10,20 98 | .db "Random from a seed value" ; print this text 99 | 100 | .db 31, 0, 0 ; TAB to 10,20 101 | .db "Press SPACE for new number\r\n" ; print this text 102 | .db "or ESC to exit\r\n" ; print this text 103 | endVDUdata: 104 | 105 | ; ------------------ 106 | ; This is where we exit the program 107 | 108 | EXIT_HERE: 109 | 110 | pop iy ; Pop all registers back from the stack 111 | pop ix 112 | pop de 113 | pop bc 114 | pop af 115 | ld hl,0 ; Load the MOS API return code (0) for no errors. 116 | ret ; Return to MOS 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | -------------------------------------------------------------------------------- /rotary_encoder_state_machine.asm: -------------------------------------------------------------------------------- 1 | ; AGON LIGHT 2 | ; Rotary Encoder test app 3 | ; Richard Turnnidge 2024 4 | ; State machine version 5 | 6 | ; Assumumption that rotary encoder is attached to 7 | ; port C, bits 0 & 1 8 | ; This could be improved for better efficiency, however 9 | ; it has been written for better understanding of state order 10 | 11 | .assume adl=1 ; big memory mode 12 | .org $40000 ; load code here 13 | 14 | jp start_here ; jump to start of code 15 | 16 | .align 64 ; MOS header 17 | .db "MOS",0,1 18 | 19 | ; --------------------------------------------- 20 | ; 21 | ; INITIAL SETUP CODE HERE 22 | ; 23 | ; --------------------------------------------- 24 | macro MOSCALL afunc 25 | ld a, afunc 26 | rst.lil $08 27 | endmacro 28 | 29 | macro TAB_TO x,y 30 | ld a, 31 ; move to... 31 | rst.lil $10 32 | ld a, x ; X position 33 | rst.lil $10 34 | ld a, y ; Y position 35 | rst.lil $10 36 | endmacro 37 | 38 | ; constants for pin states 39 | ; these are the binary values of the pins being used 40 | 41 | CLK_pin: equ 00000001b 42 | DT_pin: equ 00000010b 43 | both_pins: equ 00000011b 44 | 45 | start_here: 46 | 47 | ; store everything as good practice 48 | ; pop back when we return from code later 49 | 50 | push af 51 | push bc 52 | push de 53 | push ix 54 | push iy 55 | 56 | call setupIO 57 | 58 | showTitle: 59 | ld hl, title_str ; data to send 60 | ld bc, end_title - title_str ; length of data 61 | rst.lil $18 62 | 63 | ld a,0 ; set A to 0 64 | or a ; clear flags 65 | 66 | call updateValue 67 | 68 | ; --------------------------------------------- 69 | ; 70 | ; MAIN LOOP 71 | ; 72 | ; --------------------------------------------- 73 | 74 | MAIN_LOOP: 75 | 76 | get_key_input: 77 | MOSCALL $08 ; get IX pointer to sysvars 78 | ld a, (ix + 05h) ; ix+5h is 'last key pressed' 79 | 80 | cp 27 ; is it ESC key? 81 | jp z, exit_here ; if so exit cleanly 82 | 83 | 84 | check_encoder: 85 | ; needed to clear flags, else it didn't seem to work 86 | ld a,0 ; set A to 0 87 | or a ; clear flags 88 | in a, ($9e) ; grab current io value of port C 89 | and 00000011b ; mask just last 2 bits, pins 0 and 1 are used for the encoder 90 | ld (pin_reading), a ; store current reading 91 | 92 | and CLK_pin ; get CLK pin status 93 | ld (CLK_state), a ; store it 94 | 95 | ld a, (pin_reading) 96 | and DT_pin ; get DT pin status 97 | ld (DT_state), a ; store it 98 | 99 | or a ; clear flags to be sure 100 | 101 | ld a, (state) ; get current 'machine state' and jump to section 102 | cp 0 103 | jp z, state_0 104 | 105 | cp 1 106 | jp z, state_1 107 | 108 | cp 2 109 | jp z, state_2 110 | 111 | cp 3 112 | jp z, state_3 113 | 114 | cp 4 115 | jp z, state_4 116 | 117 | cp 5 118 | jp z, state_5 119 | 120 | cp 6 121 | jp z, state_6 122 | 123 | jp MAIN_LOOP ; shoud not get here really, but just in case... 124 | 125 | 126 | ; --------------------------------------------- 127 | ; state machine - we will be in one of these phases 128 | 129 | state_0: ; default start state 130 | 131 | ld a, (CLK_state) 132 | cp CLK_pin 133 | jr z, state_0_1 ; if CLK goes low 134 | 135 | ld a, 1 ; else set state to 1 136 | ld (state),a 137 | jp MAIN_LOOP 138 | 139 | state_0_1: 140 | 141 | ld a, (DT_state) 142 | cp DT_pin 143 | jp z, MAIN_LOOP ; if DT goes low 144 | 145 | ld a, 4 ; then set state to 4 146 | ld (state),a 147 | 148 | jp MAIN_LOOP 149 | 150 | 151 | ; --------------------------------------------- 152 | state_1: 153 | 154 | ld a, (DT_state) ; CLK went low, now check DT 155 | cp DT_pin 156 | jp z, MAIN_LOOP ; if DT goes high 157 | 158 | ld a, 2 ; set state to 2 if DT goes low 159 | ld (state),a 160 | 161 | jp MAIN_LOOP 162 | 163 | ; --------------------------------------------- 164 | state_2: 165 | 166 | ld a, (CLK_state) ; check if CLK has now gone high 167 | cp CLK_pin 168 | jp nz, MAIN_LOOP ; if CLK is low 169 | 170 | ld a, 3 ; set state to 3 if CLK is high 171 | ld (state),a 172 | 173 | jp MAIN_LOOP 174 | 175 | 176 | ; --------------------------------------------- 177 | state_3: 178 | 179 | ld a, (pin_reading) 180 | cp both_pins ; are they both high? 181 | jp nz, MAIN_LOOP ; not both 182 | 183 | ; completed one step in clockwise direction 184 | ld a, 0 185 | ld (state), a ; reset state 186 | 187 | ld a, (encoder_value) 188 | inc a 189 | ld (encoder_value), a ; increase value 190 | 191 | call updateValue ; print out change in value 192 | 193 | jp MAIN_LOOP 194 | 195 | ; --------------------------------------------- 196 | state_4: 197 | 198 | ld a, (CLK_state) ; DT went low, next check if CLK goes low 199 | cp CLK_pin 200 | jp z, MAIN_LOOP ; if CLK is high 201 | 202 | ld a, 5 ; set state to 5 if CLK is low 203 | ld (state),a 204 | 205 | jp MAIN_LOOP 206 | 207 | 208 | 209 | ; --------------------------------------------- 210 | state_5: 211 | 212 | ld a, (DT_state) ; CLK went low, next check if DT goes high 213 | cp DT_pin 214 | jp nz, MAIN_LOOP ; if DT goes low 215 | 216 | ld a, 6 ; set state to 6 if DT goes high 217 | ld (state),a 218 | 219 | jp MAIN_LOOP 220 | 221 | 222 | 223 | ; --------------------------------------------- 224 | state_6: 225 | 226 | ld a, (pin_reading) 227 | cp both_pins 228 | jp nz, MAIN_LOOP ; not both 229 | 230 | ; completed one step in anticlockwise direction 231 | ld a, 0 232 | ld (state), a ; reset state 233 | 234 | ld a, (encoder_value) 235 | dec a 236 | ld (encoder_value), a ; decrease value 237 | 238 | call updateValue ; print out change in value 239 | 240 | jp MAIN_LOOP 241 | 242 | 243 | 244 | ; --------------------------------------------- 245 | 246 | updateValue: 247 | 248 | TAB_TO 15,3 ; set cursor position 249 | ld a, (encoder_value) ; get current value of the encoder 250 | call debugDec ; print out current reading in decimal 251 | 252 | ret 253 | 254 | ; --------------------------------------------- 255 | ; 256 | ; EXIT CODE CLEANLY 257 | ; 258 | ; --------------------------------------------- 259 | 260 | exit_here: 261 | 262 | ld a, 12 263 | rst.lil $10 ; CLS 264 | ; reset all values before returning to MOS 265 | pop iy 266 | pop ix 267 | pop de 268 | pop bc 269 | pop af 270 | ld hl,0 271 | 272 | ret ; return to MOS here 273 | 274 | ; --------------------------------------------- 275 | ; 276 | ; IO PORT INIT 277 | ; 278 | ; --------------------------------------------- 279 | 280 | setupIO: 281 | ; This is all based on my reading of the eZ80 user manual. It might not all be needed. 282 | ; The default should be that all pins are treated as inputs. However, there may 283 | ; be other applications which have used the io ports before us. 284 | 285 | ; port C confugration for inputs 286 | 287 | ld a, 0 288 | out0 ($9e), a ; set DR of all port C to 0 default - 158 289 | ld a, $FF 290 | out0 ($9f), a ; set DDR of all port C to 255 - 159 291 | ld a, 0 292 | out0 ($a0), a ; set ALT1 of all port C to 0 - 160 293 | ld a, 0 294 | out0 ($a1), a ; set ALT2 of all port C to 0 - 161 295 | 296 | ret 297 | 298 | ; --------------------------------------------- 299 | ; 300 | ; DATA & STRINGS 301 | ; 302 | ; --------------------------------------------- 303 | 304 | state: .db 0 305 | pin_reading: .db 0 306 | CLK_state: .db 0 307 | DT_state: .db 0 308 | 309 | encoder_value: .db 0 310 | 311 | title_str: 312 | .db 12 ; CLS 313 | .db 31,0,0 ; TAB to 0,0 314 | .db "Rotary Encoder - state machine" ; text to show 315 | 316 | .db 31,0,3 ; TAB to 0,0 317 | .db "Encoder value:" ; text to show 318 | 319 | .db 31,0,13 ; TAB to 0,0 320 | .db "Hit ESC to exit" ; text to show 321 | 322 | .db 31,0,15 ; TAB to 0,0 323 | .db "Richard Turnnidge 2024" ; text to show 324 | .db 31,0,17 ; TAB to 0,0 325 | .db "No warranty provided!" ; text to show 326 | 327 | end_title: 328 | 329 | 330 | ; --------------------------------------------- 331 | ; 332 | ; DEBUG IN DECIMAL 333 | ; 334 | ; --------------------------------------------- 335 | 336 | debugDec: ; debug A to screen as 3 char string pos 337 | 338 | push af 339 | ld a, 48 ; ascii for '0' 340 | ld (answer),a 341 | ld (answer+1),a 342 | ld (answer+2),a ; reset to default before starting 343 | pop af 344 | 345 | ld (base),a ; base char set to '0' 346 | 347 | cp 200 348 | jr c,_under200 ; not 200+ 349 | 350 | sub a, 200 ; is over 200 351 | ld (base),a ; sub 200 and save 352 | 353 | ld a, 50 ; 2 in ascii 354 | ld (answer),a 355 | jr _under100 356 | 357 | _under200: 358 | cp 100 359 | jr c,_under100 ; not 100+ 360 | sub a, 100 361 | ld (base),a ; sub 100 and save 362 | 363 | ld a, 49 ; 1 in ascii 364 | ld (answer),a 365 | jr _under100 366 | 367 | 368 | _under100: 369 | ld a, (base) 370 | ld c,a 371 | ld d, 10 372 | call C_Div_D 373 | 374 | add a, 48 375 | ld (answer + 2),a 376 | 377 | ld a, c 378 | add a, 48 379 | ld (answer + 1),a 380 | 381 | 382 | ld hl, debugOut ; address of string to use 383 | ld bc, endDebugOut - debugOut ; length of string 384 | rst.lil $18 385 | ret 386 | 387 | 388 | debugOut: 389 | answer: .db "000" ; string to output 390 | endDebugOut: 391 | 392 | base: .db 0 ; used in calculations 393 | 394 | 395 | ; --------------------------------------------- 396 | ; 397 | ; MATHS ROUTINES 398 | ; 399 | ; --------------------------------------------- 400 | 401 | ; C divided by D 402 | 403 | ;Inputs: 404 | ; C is the numerator 405 | ; D is the denominator 406 | ;Outputs: 407 | ; A is the remainder 408 | ; B is 0 409 | ; C is the result of C/D 410 | ; D,E,H,L are not changed 411 | ; 412 | 413 | C_Div_D: 414 | 415 | ld b,8 416 | xor a 417 | sla c 418 | rla 419 | cp d 420 | jr c,$+4 421 | inc c 422 | sub d 423 | djnz $-8 424 | ret 425 | 426 | ; --------------------------------------------- 427 | ; 428 | ; END 429 | ; 430 | ; --------------------------------------------- 431 | -------------------------------------------------------------------------------- /savedata.asm: -------------------------------------------------------------------------------- 1 | ; extra MACRO files need to go here 2 | include "myMacros.inc" 3 | 4 | .assume adl=1 ; ez80 ADL memory mode 5 | .org $40000 ; load code here 6 | 7 | jp start_here ; jump to start of code 8 | 9 | .align 64 ; MOS header 10 | .db "MOS",0,1 11 | 12 | start_here: 13 | 14 | push af ; store all the registers 15 | push bc 16 | push de 17 | push ix 18 | push iy 19 | 20 | ; ------------------ 21 | ; This is our actual code 22 | ; try loading the file 23 | ld hl, filename ; file name 24 | ld de, lastKey ; were to load data 25 | ld bc, 1 ; num bytes to read 26 | 27 | MOSCALL $01 ; try to open and read byte from file 28 | 29 | ld hl, startText ; print initial text 30 | call printString 31 | 32 | ld a, (lastKey) 33 | rst.lil $10 ; print last key saved to file 34 | 35 | ld hl, newline ; print initial text 36 | call printString 37 | 38 | wait_for_keyup: ; wait for key up state so we only do it once 39 | MOSCALL $08 ; get IX pointer to sysvars 40 | ld a, (ix + 18h) ; get key state 41 | cp 0 ; are keys up, none currently pressed? 42 | jr nz, wait_for_keyup ; loop if there is a key still pressed 43 | 44 | 45 | LOOP_HERE: ; loop here until we press a key 46 | 47 | MOSCALL $08 ; get IX pointer to sysvars 48 | ld a, (ix + 18h) ; get key state, 0 or 1 49 | cp 0 ; are keys up, none currently pressed? 50 | jr z, LOOP_HERE ; nothing is currently pressed 51 | 52 | ; a key was pressed, so fetch it 53 | MOSCALL $08 ; get IX pointer to sysvars 54 | ld a, (ix + 05h) ; ix+5h is 'last key pressed' 55 | ld (lastKey),a ; store the key value ready to save 56 | 57 | ; we need to delete the old file first 58 | ld hl, filename ; file name 59 | MOSCALL $05 ; delete call 60 | 61 | ld a, 00010000b ; wait a moment for SD to catch up 62 | call multiPurposeDelay 63 | ; save new file 64 | ld hl, filename ; file name 65 | ld de, lastKey ; data to save 66 | ld bc, 1 ; num bytes to save 67 | 68 | MOSCALL $02 ; save the new file, then EXIT 69 | 70 | ; ------------------ 71 | ; This is where we exit the program 72 | 73 | EXIT_HERE: 74 | 75 | ld hl, exitText 76 | call printString 77 | 78 | pop iy ; Pop all registers back from the stack 79 | pop ix 80 | pop de 81 | pop bc 82 | pop af 83 | ld hl,0 ; Load the MOS API return code (0) for no errors. 84 | ret ; Return to MOS 85 | 86 | 87 | ; ------------------------------------ 88 | ; DATA AND FUNCTIONS 89 | ; ------------------------------------ 90 | 91 | filename: .db "savedata.ini",0 ; prefs file storing high score 92 | lastKey: .db 32 93 | 94 | ; ------------------ 95 | ; This is the text we send to VDP 96 | 97 | startText: 98 | .db "Last time you pressed: ",0 ; print this text 99 | 100 | newline: 101 | .db "\r\nPress another key...\r\n",0 102 | 103 | exitText: 104 | .db "Thanks, Goodbye\r\n",0 ; print this text 105 | 106 | ; ------------------ 107 | 108 | printString: ; print zero terminated string 109 | ld a,(hl) 110 | or a 111 | ret z 112 | RST.LIL 10h 113 | inc hl 114 | jr printString 115 | 116 | ; ------------------ 117 | 118 | multiPurposeDelay: ; routine to wait for a given time 119 | push bc 120 | ld b, a 121 | MOSCALL $08 ; get IX pointer to sysvars 122 | 123 | waitLoop: 124 | 125 | ld a, (ix + 0) ; ix+0h is lowest byte of clock timer 126 | 127 | ; we check if bit set is same as last time we checked. 128 | ; bit 0 - don't use 129 | ; bit 1 - changes 64 times per second 130 | ; bit 2 - changes 32 times per second 131 | ; bit 3 - changes 16 times per second 132 | 133 | ; bit 4 - changes 8 times per second 134 | ; bit 5 - changes 4 times per second 135 | ; bit 6 - changes 2 times per second 136 | ; bit 7 - changes 1 times per second 137 | and b 138 | ld c,a 139 | ld a, (oldTimeStamp) 140 | cp c ; is A same as last value? 141 | jr z, waitLoop ; loop here if it is 142 | ld a, c 143 | ld (oldTimeStamp), a ; set new value 144 | 145 | pop bc 146 | ret 147 | 148 | oldTimeStamp: .db 00h 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | -------------------------------------------------------------------------------- /scroll.asm: -------------------------------------------------------------------------------- 1 | ; extra MACRO files need to go here 2 | include "myMacros.inc" 3 | 4 | .assume adl=1 ; ez80 ADL memory mode 5 | .org $40000 ; load code here 6 | 7 | jp start_here ; jump to start of code 8 | 9 | .align 64 ; MOS header 10 | .db "MOS",0,1 11 | 12 | start_here: 13 | 14 | push af ; store all the registers 15 | push bc 16 | push de 17 | push ix 18 | push iy 19 | 20 | ; ------------------ 21 | ; This is our actual code 22 | 23 | ; prepare the screen 24 | 25 | SET_MODE 8 ; mode 8 is 640x480 pixels, 64 colours 26 | 27 | 28 | ; Sending a VDU byte stream 29 | 30 | ld hl, VDUdata ; address of string to use 31 | ld bc, endVDUdata - VDUdata ; length of string 32 | rst.lil $18 ; Call the MOS API to send data to VDP 33 | 34 | ld a, $08 ; code to send to MOS 35 | rst.lil $08 ; get IX pointer to System Variables 36 | 37 | ; ------------------ 38 | 39 | LOOP_HERE: ; loop here until we hit ESC key 40 | 41 | MOSCALL $1E ; load IX with keymap address 42 | 43 | ld a, (ix + $0E) 44 | bit 0, a ; index $0E, bit 0 is for ESC key in matrix 45 | jp nz, EXIT_HERE ; if pressed, ESC key to exit 46 | 47 | MOSCALL $1E ; load IX with keymap address 48 | ld a, (ix + $03) 49 | bit 1, a ; index $06 bit 0 is for 'left' key in matrix 50 | call nz, scroll_left ; if pressed, setFrame1 51 | 52 | MOSCALL $1E ; load IX with keymap address 53 | ld a, (ix + $0F) 54 | bit 1, a ; index $06 bit 0 is for 'left' key in matrix 55 | call nz, scroll_right ; if pressed, setFrame1 56 | 57 | MOSCALL $1E ; load IX with keymap address 58 | ld a, (ix + $07) 59 | bit 1, a ; index $06 bit 0 is for 'left' key in matrix 60 | call nz, scroll_up ; if pressed, setFrame1 61 | 62 | MOSCALL $1E ; load IX with keymap address 63 | ld a, (ix + $05) 64 | bit 1, a ; index $06 bit 0 is for 'left' key in matrix 65 | call nz, scroll_down ; if pressed, setFrame1 66 | 67 | jp LOOP_HERE 68 | 69 | ; ------------------ 70 | 71 | scroll_left: 72 | ld a, 1 73 | ld (scrollDirection), a 74 | call doScroll 75 | ret 76 | 77 | scroll_right: 78 | ld a, 0 79 | ld (scrollDirection), a 80 | call doScroll 81 | ret 82 | 83 | scroll_up: 84 | ld a, 3 85 | ld (scrollDirection), a 86 | call doScroll 87 | ret 88 | 89 | scroll_down: 90 | ld a, 2 91 | ld (scrollDirection), a 92 | call doScroll 93 | ret 94 | 95 | ; ------------------ 96 | 97 | doScroll: 98 | ld hl, scrollData ; address of string to use 99 | ld bc, endScrollData - scrollData ; length of string 100 | rst.lil $18 ; Call the MOS API to send data to VDP 101 | 102 | ld a, 00010000b 103 | call multiPurposeDelay 104 | 105 | 106 | ret 107 | 108 | ; Scroll the screen 109 | ; VDU 23, 7, extent, direction, speed 110 | 111 | scrollData: 112 | .db 23, 7, 2 ; extent: 0 = text viewport, 1 = whole screen 113 | ; 2 = graphics viewport 114 | scrollDirection: .db 0 ; direction: 0 right, 1 left, 2 down, 3 up 115 | .db 1 ; speed = number of pixels 116 | endScrollData: 117 | 118 | 119 | ; ------------------ 120 | ; This is where we exit the program 121 | 122 | EXIT_HERE: 123 | 124 | CLS 125 | pop iy ; Pop all registers back from the stack 126 | pop ix 127 | pop de 128 | pop bc 129 | pop af 130 | ld hl,0 ; Load the MOS API return code (0) for no errors. 131 | ret ; Return to MOS 132 | 133 | ; ------------------ 134 | ; This is the data we send to VDP 135 | 136 | VDUdata: 137 | .db 23, 0, 192, 0 ; set to non-scaled graphics 138 | 139 | .db 17, 128 +7 ; background text black 140 | .db 12 ; cls 141 | 142 | .db 24 ; set graphics viewport:- 143 | .dw 40, 150, 270, 40 ; 24, left; bottom; right; top; 144 | 145 | .db 17, 128 ; background text grey 146 | .db 12 ; cls 147 | ; FOR A SINGLE PIXEL PLOT 148 | 149 | .db 18, 0, bright_red ; set graphics colour: mode (0), colour 150 | 151 | .db 25, 69 ; PLOT: mode (69 is a point in current colour), 152 | .dw 200,80 ; X; Y; 153 | 154 | ; FOR A LINE 155 | 156 | .db 18, 0, bright_magenta ; set graphics colour: mode (0), colour 157 | 158 | .db 25, 69 ; PLOT: mode (69 is a point in current colour), 159 | .dw 300, 60 ; X; Y; 160 | 161 | .db 25, 13 ; PLOT: mode (13 is a line), 162 | .dw 250,130 ; X; Y; 163 | 164 | ; FOR A RECTANGLE 165 | 166 | .db 18, 0, green ; set graphics colour: mode (0), colour 167 | 168 | .db 25, 69 ; PLOT: mode (69 is a point in current colour), 169 | .dw 10,120 ; X; Y; 170 | 171 | .db 25, 101 ; PLOT: mode (101 is a filled rectangle), 172 | .dw 100,180 ; X; Y; 173 | 174 | 175 | ; FOR A CIRCLE 176 | 177 | .db 18, 0, bright_yellow ; set graphics colour: mode (0), colour 178 | 179 | .db 25, 68 ; PLOT: mode (69 is a MOVE TO but don't plot point), 180 | .dw 180,140 ; X; Y; 181 | 182 | .db 25, 149 ; PLOT: mode (149 is an outlined circle), 183 | .dw 200,160 ; X; Y; 184 | 185 | ; FOR A FILLED TRIANGLE 186 | 187 | .db 18, 0, blue ; set graphics colour: mode (0), colour 188 | 189 | .db 25, 69 ; PLOT: mode (69 is a point in current colour), 190 | .dw 10,10 ; X; Y; 191 | 192 | .db 25, 69 ; PLOT: mode (69 is a point in current colour), 193 | .dw 50, 100 ; X; Y; 194 | 195 | .db 25, 85 ; PLOT: mode (85 is a filled triangle), 196 | .dw 200,20 ; X; Y; 197 | 198 | 199 | endVDUdata: 200 | 201 | ; ------------------ 202 | ; colour data 203 | 204 | bright_red: equ 9 205 | green: equ 2 206 | bright_yellow: equ 11 207 | bright_magenta: equ 13 208 | blue: equ 4 209 | white: equ 7 210 | black: equ 0 211 | bright_white: equ 15 212 | 213 | ; ------------------ 214 | 215 | multiPurposeDelay: 216 | push bc 217 | ld b, a 218 | MOSCALL $08 ; get IX pointer to sysvars 219 | 220 | waitLoop: 221 | 222 | ld a, (ix + 0) ; ix+0h is lowest byte of clock timer 223 | 224 | ; we check if bit set is same as last time we checked. 225 | ; bit 0 - don't use 226 | ; bit 1 - changes 64 times per second 227 | ; bit 2 - changes 32 times per second 228 | ; bit 3 - changes 16 times per second 229 | 230 | ; bit 4 - changes 8 times per second 231 | ; bit 5 - changes 4 times per second 232 | ; bit 6 - changes 2 times per second 233 | ; bit 7 - changes 1 times per second 234 | and b 235 | ld c,a 236 | ld a, (oldTimeStamp) 237 | cp c ; is A same as last value? 238 | jr z, waitLoop ; loop here if it is 239 | ld a, c 240 | ld (oldTimeStamp), a ; set new value 241 | 242 | pop bc 243 | ret 244 | 245 | oldTimeStamp: .db 00h 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | -------------------------------------------------------------------------------- /showkey.asm: -------------------------------------------------------------------------------- 1 | ; extra MACRO files need to go here 2 | include "myMacros.inc" 3 | 4 | .assume adl=1 ; ez80 ADL memory mode 5 | .org $40000 ; load code here 6 | 7 | jp start_here ; jump to start of code 8 | 9 | .align 64 ; MOS header 10 | .db "MOS",0,1 11 | 12 | ; extra code files need to go here, or later 13 | include "debug.asm" 14 | 15 | start_here: 16 | 17 | push af ; store all the registers 18 | push bc 19 | push de 20 | push ix 21 | push iy 22 | 23 | ; ------------------ 24 | ; This is our actual code 25 | 26 | CLS 27 | 28 | ld a, 23 ; HIDE CURSOR 29 | rst.lil $10 30 | ld a, 1 31 | rst.lil $10 32 | ld a,0 ; VDU 23,1,0 = hide the text cursor 33 | rst.lil $10 ; VDU 23,1,1 = show the text cursor 34 | 35 | ; From 'LESSON 03' 36 | ld a, $08 ; code to send to MOS... 37 | rst.lil $08 ; get IX pointer to System Variables 38 | 39 | WAIT_HERE: ; loop here until we hit ESC key 40 | ld a, (ix + $05) ; get ASCII code of LAST key pressed 41 | cp 27 ; check if 27 (ascii code for ESC) 42 | jp z, EXIT_HERE ; if pressed, jump to EXIT_HERE 43 | 44 | ld b,10 ; set the X position 45 | ld c,5 ; set the Y position 46 | call printHexA ; display HEX value of A, at TAB B, C 47 | 48 | jr WAIT_HERE 49 | 50 | ; ------------------ 51 | ; This is where we exit the program 52 | 53 | EXIT_HERE: 54 | 55 | ld a, 23 ; SHOW CURSOR 56 | rst.lil $10 57 | ld a, 1 58 | rst.lil $10 59 | ld a,1 ; VDU 23,1,0 = hide the text cursor 60 | rst.lil $10 ; VDU 23,1,1 = show the text cursor 61 | 62 | CLS 63 | pop iy ; Pop all registers back from the stack 64 | pop ix 65 | pop de 66 | pop bc 67 | pop af 68 | ld hl,0 ; Load the MOS API return code (0) for no errors. 69 | ret ; Return to MOS 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | -------------------------------------------------------------------------------- /slowdown.asm: -------------------------------------------------------------------------------- 1 | ; extra MACRO files need to go here 2 | include "myMacros.inc" 3 | 4 | .assume adl=1 ; ez80 ADL memory mode 5 | .org $40000 ; load code here 6 | 7 | jp start_here ; jump to start of code 8 | 9 | .align 64 ; MOS header 10 | .db "MOS",0,1 11 | 12 | ; extra code files need to go here, or later 13 | include "debug.asm" 14 | 15 | start_here: 16 | 17 | push af ; store all the registers 18 | push bc 19 | push de 20 | push ix 21 | push iy 22 | 23 | ; ------------------ 24 | ; This is our actual code 25 | 26 | ld hl, VDUdata ; address of string to use 27 | ld bc, endVDUdata - VDUdata ; length of string 28 | rst.lil $18 ; display message 29 | 30 | ld d, 0 ; Set D, our counter 31 | 32 | LOOP_HERE: ; loop here until we hit ESC or SPACE key 33 | 34 | MOSCALL $1E ; load IX with keymap address 35 | ld a, (ix + $0E) 36 | bit 0, a ; index $0E, bit 0 is for ESC key in matrix 37 | jp nz, EXIT_HERE ; if pressed, ESC key to exit 38 | 39 | ld a, 00100000b ; put a bit value into A. Only set ONE bit 40 | call multiPurposeDelay ; call the delay routine 41 | 42 | 43 | ld a, d ; store the counter in A 44 | 45 | ld b, 0 ; x position in B 46 | ld c, 6 ; y position in C 47 | call printHexA ; print A to screen in HEX 48 | 49 | inc d 50 | 51 | jr LOOP_HERE ; go round the loop 52 | 53 | 54 | 55 | ; ------------------ 56 | ; This is the data we send to VDP 57 | 58 | VDUdata: 59 | .db 22,8 ; set screen to MODE 8, this will also clear screen 60 | .db 31, 0, 0 ; TAB to 10,20 61 | .db "Clock based delay" ; print this text 62 | 63 | .db 31, 0, 2 ; TAB to 10,20 64 | .db "Press ESC to exit\r\n" ; print this text 65 | endVDUdata: 66 | 67 | ; ------------------ 68 | ; This is where we exit the program 69 | 70 | EXIT_HERE: 71 | 72 | pop iy ; Pop all registers back from the stack 73 | pop ix 74 | pop de 75 | pop bc 76 | pop af 77 | ld hl,0 ; Load the MOS API return code (0) for no errors. 78 | ret ; Return to MOS 79 | 80 | 81 | 82 | 83 | 84 | 85 | ; ------------------ 86 | ; delay routine 87 | 88 | ; routine waits a fixed time, then returns 89 | ; arrive with A = the delay byte. One bit to be set only. 90 | ; eg. ld A, 00000100b 91 | 92 | multiPurposeDelay: 93 | push bc 94 | ld b, a 95 | MOSCALL $08 ; get IX pointer to sysvars 96 | 97 | waitLoop: 98 | 99 | ld a, (ix + 0) ; ix+0h is lowest byte of clock timer 100 | 101 | ; we check if bit set is same as last time we checked. 102 | ; bit 0 - don't use 103 | ; bit 1 - changes 64 times per second 104 | ; bit 2 - changes 32 times per second 105 | ; bit 3 - changes 16 times per second 106 | 107 | ; bit 4 - changes 8 times per second 108 | ; bit 5 - changes 4 times per second 109 | ; bit 6 - changes 2 times per second 110 | ; bit 7 - changes 1 times per second 111 | and b 112 | ld c,a 113 | ld a, (oldTimeStamp) 114 | cp c ; is A same as last value? 115 | jr z, waitLoop ; loop here if it is 116 | ld a, c 117 | ld (oldTimeStamp), a ; set new value 118 | 119 | pop bc 120 | ret 121 | 122 | oldTimeStamp: .db 00h 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | ld a, 00100000b ; put a bit value into A. Only set ONE bit 154 | call multiPurposeDelay ; call the delay routine 155 | -------------------------------------------------------------------------------- /sound.asm: -------------------------------------------------------------------------------- 1 | ; extra MACRO files need to go here 2 | include "myMacros.inc" 3 | 4 | .assume adl=1 ; ez80 ADL memory mode 5 | .org $40000 ; load code here 6 | 7 | jp start_here ; jump to start of code 8 | 9 | .align 64 ; MOS header 10 | .db "MOS",0,1 11 | 12 | start_here: 13 | 14 | push af ; store all the registers 15 | push bc 16 | push de 17 | push ix 18 | push iy 19 | 20 | ; ------------------ 21 | ; This is our actual code 22 | 23 | CLS 24 | call loadSample 25 | 26 | ld hl, message 27 | call printString 28 | 29 | WAIT_HERE: 30 | 31 | MOSCALL $1E ; get IX pointer to keyvals 32 | ld a, (ix + $0E) 33 | bit 0, a ; ESC key 34 | jp nz, EXIT_HERE ; if pressed, jump to EXIT_HERE 35 | 36 | MOSCALL $1E ; get IX pointer to keyvals 37 | ld a, (ix + $08) 38 | bit 1, a ; A key 39 | call nz, playNote 40 | 41 | MOSCALL $1E ; get IX pointer to keyvals 42 | ld a, (ix + $0C) 43 | bit 4, a ; B key 44 | call nz, playMainJet ; if pressed, start sound 45 | 46 | MOSCALL $1E ; get IX pointer to keyvals 47 | ld a, (ix + $0C) 48 | bit 4, a ; B key 49 | call z, stopMainJet ; if not pressed stop sound 50 | 51 | MOSCALL $1E ; get IX pointer to keyvals 52 | ld a, (ix + $0A) 53 | bit 2, a ; C key 54 | call nz, playSample 55 | 56 | jr WAIT_HERE 57 | 58 | ; ------------------ 59 | ; This is where we exit the program 60 | 61 | EXIT_HERE: 62 | 63 | ld a, 23 ; SHOW CURSOR 64 | rst.lil $10 65 | ld a, 1 66 | rst.lil $10 67 | ld a,1 ; VDU 23,1,0 = hide the text cursor 68 | rst.lil $10 ; VDU 23,1,1 = show the text cursor 69 | 70 | CLS 71 | pop iy ; Pop all registers back from the stack 72 | pop ix 73 | pop de 74 | pop bc 75 | pop af 76 | ld hl,0 ; Load the MOS API return code (0) for no errors. 77 | ret ; Return to MOS 78 | 79 | 80 | ; ------------------ 81 | ; DATA 82 | ; ------------------ 83 | 84 | message: 85 | .db "Sound example, press A, B, or C\r\n",0 86 | 87 | ; ------------------ 88 | ; SOUND ROUTINES 89 | ; ------------------ 90 | 91 | playNote: 92 | ld hl, note 93 | ld bc, endNote - note 94 | rst.lil $18 95 | call wait_for_keyup 96 | ret 97 | 98 | note: 99 | .db 23,0,$85 ; do sound 100 | .db 0 ; channel 101 | .db 4,0 ; set waveform, waveform type 102 | 103 | .db 23,0,$85 ; do sound 104 | .db 0 ; channel 105 | .db 0,63 ; code, volume 106 | .dw 800 ; freq 107 | .dw 1000 ; duration (milliseconds WORD) 108 | endNote: 109 | 110 | ; ------------------ 111 | 112 | playMainJet: 113 | ld a, (jetPlaying) 114 | cp 1 115 | ret z 116 | 117 | ld hl, mainJetPlay 118 | ld bc, endMainJet - mainJetPlay 119 | rst.lil $18 120 | 121 | ld a, 1 122 | ld (jetPlaying),a 123 | ret 124 | 125 | mainJetPlay: 126 | .db 23,0,$85,1,4,5 ; set waveform to VIC noise 127 | .db 23,0,$85,1,0,127 128 | .dw $40, -1 ; frq, duration 129 | endMainJet: 130 | 131 | 132 | stopMainJet: 133 | ld hl, stopMainSnd 134 | ld bc, endMainSnd - stopMainSnd 135 | rst.lil $18 136 | 137 | ld a, 0 138 | ld (jetPlaying),a 139 | ret 140 | 141 | stopMainSnd: 142 | .db 23,0,$85,1,2,0 ; set vol to 0 143 | endMainSnd: 144 | 145 | jetPlaying: 146 | .db 0 147 | 148 | ; ------------------ 149 | 150 | playSample: 151 | ld hl, startSample 152 | ld bc, endSample - startSample 153 | rst.lil $18 154 | call wait_for_keyup 155 | ret 156 | 157 | startSample: 158 | .db 23,0,$85 ; do sound 159 | .db 2,4,-1 ; channel 160 | 161 | .db 23,0,$85 ; do sound 162 | .db 2,0, 127 ; channel, volume 163 | .dw 125 ; freq (ignored for samples) 164 | .dw 1000 ; duration (ignored for samples) 165 | endSample: 166 | 167 | ; ------------------ 168 | 169 | loadSample: ; for loading audio samples 170 | ld hl, sampleStr ; start of data to send 171 | ld bc, endSampleStr - sampleStr ; length of data to send 172 | rst.lil $18 ; send data 173 | ret 174 | 175 | sampleStr: 176 | .db 23,0,85h ; audio command 177 | .db -1, 5 ; sample number, sample management 178 | .db 0 ; load 179 | .dl 15279 ; length in bytes (LONG) 180 | incbin "letsgo.raw" 181 | endSampleStr: 182 | 183 | ; ------------------ 184 | ; OTHER FUNCTIONS 185 | ; ------------------ 186 | 187 | wait_for_keyup: ; wait for key up state so we only do it once 188 | MOSCALL $08 ; get IX pointer to sysvars 189 | ld a, (ix + 18h) ; get key state 190 | cp 0 ; are keys up, none currently pressed? 191 | jr nz, wait_for_keyup ; loop if there is a key still pressed 192 | ret 193 | 194 | ; ------------------ 195 | 196 | printString: ; print zero terminated string 197 | ld a,(hl) 198 | or a 199 | ret z 200 | RST.LIL 10h 201 | inc hl 202 | jr printString 203 | 204 | ; ------------------ 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | -------------------------------------------------------------------------------- /sprite.asm: -------------------------------------------------------------------------------- 1 | ; extra MACRO files need to go here 2 | include "myMacros.inc" 3 | 4 | .assume adl=1 ; ez80 ADL memory mode 5 | .org $40000 ; load code here 6 | 7 | jp start_here ; jump to start of code 8 | 9 | .align 64 ; MOS header 10 | .db "MOS",0,1 11 | 12 | start_here: 13 | 14 | push af ; store all the registers 15 | push bc 16 | push de 17 | push ix 18 | push iy 19 | 20 | ; ------------------ 21 | ; This is our actual code 22 | 23 | ; prepare the screen 24 | 25 | SET_MODE 8 ; mode 8 is 320x240 pixels, 64 colours 26 | 27 | 28 | ; Sending a VDU byte stream 29 | 30 | ld hl, VDUdata ; address of string to use 31 | ld bc, endVDUdata - VDUdata ; length of string 32 | rst.lil $18 ; Call the MOS API to send data to VDP 33 | 34 | ld a, $08 ; code to send to MOS 35 | rst.lil $08 ; get IX pointer to System Variables 36 | 37 | WAIT_HERE: ; loop here until we hit ESC key 38 | 39 | MOSCALL $1E ; load IX with keymap address 40 | ld a, (ix + $0E) 41 | bit 0, a ; index $0E, bit 0 is for ESC key in matrix 42 | jp nz, EXIT_HERE ; if pressed, ESC key to exit 43 | 44 | ld a, (ix + $06) 45 | bit 0, a ; index $06 bit 0 is for '1' key in matrix 46 | call nz, setFrame0 ; if pressed, setFrame1 47 | 48 | ld a, (ix + $06) 49 | bit 1, a ; index $06 bit 1 is for '2' key in matrix 50 | call nz, setFrame1 ; if pressed, setFrame2 51 | 52 | ld a, (ix + $0A) 53 | bit 4, a ; index $0A bit 4 is for 'h' key in matrix 54 | call nz, hideSprite ; if pressed, setFrame2 55 | 56 | ld a, (ix + $0A) 57 | bit 1, a ; index $0A bit 1 is for 's' key in matrix 58 | call nz, showSprite ; if pressed, setFrame2 59 | 60 | jp WAIT_HERE 61 | 62 | ; ------------------ 63 | 64 | setFrame0: 65 | ld hl, set0 ; address of string to use 66 | ld bc, endset0 - set0 ; length of string 67 | rst.lil $18 ; Call the MOS API to send data to VDP 68 | ret 69 | 70 | set0: 71 | .db 23, 27, 4, 0 ; select sprite 0 72 | .db 23, 27, 10, 0 ; select frame 0 73 | endset0: 74 | 75 | ; ------------------ 76 | 77 | setFrame1: 78 | ld hl, set1 ; address of string to use 79 | ld bc, endset1 - set1 ; length of string 80 | rst.lil $18 ; Call the MOS API to send data to VDP 81 | ret 82 | 83 | set1: 84 | .db 23, 27, 4, 0 ; select sprite 0 85 | .db 23, 27, 10, 1 ; select frame 1 86 | endset1: 87 | 88 | ; ------------------ 89 | 90 | hideSprite: 91 | ld hl, hide ; address of string to use 92 | ld bc, endhide - hide ; length of string 93 | rst.lil $18 ; Call the MOS API to send data to VDP 94 | ret 95 | 96 | hide: 97 | .db 23, 27, 4, 0 ; select sprite 0 98 | .db 23, 27, 12 ; hide current sprite 99 | endhide: 100 | 101 | ; ------------------ 102 | 103 | showSprite: 104 | ld hl, show ; address of string to use 105 | ld bc, endshow - show ; length of string 106 | rst.lil $18 ; Call the MOS API to send data to VDP 107 | ret 108 | 109 | show: 110 | .db 23, 27, 4, 0 ; select sprite 0 111 | .db 23, 27, 11 ; show current sprite 112 | endshow: 113 | 114 | 115 | ; ------------------ 116 | ; This is where we exit the program 117 | 118 | EXIT_HERE: 119 | 120 | CLS 121 | pop iy ; Pop all registers back from the stack 122 | pop ix 123 | pop de 124 | pop bc 125 | pop af 126 | ld hl,0 ; Load the MOS API return code (0) for no errors. 127 | ret ; Return to MOS 128 | 129 | ; ------------------ 130 | ; This is the data we send to VDP 131 | 132 | crystal: EQU 0 ; used for bitmap ID number 133 | star: EQU 1 ; used for bitmap ID number 134 | our_sprite: EQU 0 ; sprite ID - always start at 0 upwards 135 | 136 | VDUdata: 137 | .db 23, 0, 192, 0 ; set to non-scaled graphics 138 | 139 | ; LOAD THE BITMAP FROM A FILE 140 | ; file must be 24bit colour plus 8 bit alpha, byte order: RGBA 141 | ; 16x16 pixels RGBA should be 1,024 bytes in size 142 | 143 | .db 23, 27, 0, crystal ; select bitmap 0 - crystal 144 | .db 23, 27, 1 ; load bitmap data... 145 | .dw 16, 16 ; of size 16x16, from file: 146 | incbin "crystal.data" 147 | 148 | .db 23, 27, 0, star ; select bitmap 1 - star 149 | .db 23, 27, 1 ; load bitmap data... 150 | .dw 16, 16 ; of size 16x16, from file: 151 | incbin "star.data" 152 | 153 | 154 | ; SETUP THE SPRITE 155 | 156 | .db 23, 27, 4, our_sprite ; select sprite 0 157 | .db 23, 27, 5 ; clear frames 158 | .db 23, 27, 6, crystal ; add bitmap frame crystal to sprite 159 | .db 23, 27, 6, star ; add bitmap frame star to sprite 160 | .db 23, 27, 7, 1 ; activate 1 sprite(s) 161 | .db 23, 27, 11 ; show current sprite 162 | 163 | ; MOVE A SPRITE 164 | 165 | .db 23, 27, 4, our_sprite ; select sprite 0 166 | .db 23, 27, 13 ; move currrent sprite to... 167 | .dw 150, 100 ; x,y (as words) 168 | 169 | .db 23, 27, 15 ; update sprites in GPU 170 | 171 | 172 | ; PLOT A RECTANGLE 173 | 174 | .db 18, 0, 45 ; set graphics colour: mode (0), colour 45 = purple 175 | 176 | .db 25, 69 ; PLOT: mode (69 is a point in current colour), 177 | .dw 80,80 ; X; Y; 178 | 179 | .db 25, 101 ; PLOT: mode (101 is a filled rectangle), 180 | .dw 190,130 ; X; Y; 181 | 182 | endVDUdata: 183 | 184 | ; ------------------ 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | -------------------------------------------------------------------------------- /star.data: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/richardturnnidge/lessons/3cb69781f37a25d3ff7da6e31cd59a7ee4dc19b5/star.data -------------------------------------------------------------------------------- /text.asm: -------------------------------------------------------------------------------- 1 | .assume adl=1 ; ez80 ADL memory mode 2 | .org $40000 ; load code here 3 | 4 | jp start_here ; jump to start of code 5 | 6 | .align 64 ; MOS header 7 | .db "MOS",0,1 8 | 9 | start_here: 10 | 11 | push af ; store all the registers 12 | push bc 13 | push de 14 | push ix 15 | push iy 16 | 17 | ; ------------------ 18 | ; This is our actual code 19 | 20 | ; Sending VDU commands byte by byte 21 | 22 | ld a, 22 23 | rst.lil $10 ; set screen mode... 24 | ld a, 8 25 | rst.lil $10 ; to mode 8, 320x240 64 colours 26 | 27 | 28 | ; Sending a VDU byte stream 29 | 30 | ld hl, VDUdata ; address of string to use 31 | ld bc, endVDUdata - VDUdata ; length of string 32 | rst.lil $18 ; Call the MOS API to send data to VDP 33 | 34 | ; ------------------ 35 | ; This is where we exit the program 36 | 37 | pop iy ; Pop all registers back from the stack 38 | pop ix 39 | pop de 40 | pop bc 41 | pop af 42 | ld hl,0 ; Load the MOS API return code (0) for no errors. 43 | ret ; Return to MOS 44 | 45 | ; ------------------ 46 | ; This is the data we send to VDP 47 | 48 | VDUdata: 49 | .db 17, 0 ; set text colour, 0 = black, 1 = red, etc 50 | .db 17, 11 + 128 ; set text background, +128 for background 51 | .db 31, 10, 10 ; TAB to 10,20 52 | .db "Hello Agon coders\r\n" ; print this text 53 | .db 17, 15 ; reset text colour, 15 = bright white 54 | .db 17, 0 + 128 ; reset text background, +128 for background 55 | endVDUdata: 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /textinput.asm: -------------------------------------------------------------------------------- 1 | .assume adl=1 ; ez80 ADL memory mode 2 | .org $40000 ; load code here 3 | 4 | jp start_here ; jump to start of code 5 | 6 | .align 64 ; MOS header 7 | .db "MOS",0,1 8 | 9 | include "myMacros.inc" 10 | 11 | start_here: 12 | 13 | push af ; store all the registers 14 | push bc 15 | push de 16 | push ix 17 | push iy 18 | 19 | ; ------------------ 20 | ; This is our actual code 21 | CLS ; just CLS 22 | 23 | ld hl, VDUdata ; address of string to use 24 | ld bc, endVDUdata - VDUdata ; length of string 25 | rst.lil $18 ; display message 26 | 27 | ld hl, textBuffer ; HL needs to point to where text will be stored 28 | ld bc, 10 ; BC is maximum nunber of chars 29 | ld e, 1 ; 1 to clear buffer, 0 not to clear 30 | MOSCALL $09 ; call $09 mos_editline 31 | 32 | cp 27 ; A gives return code: 27 ESC, 13 CR 33 | jr z, hitEscape 34 | 35 | hitEnter: 36 | ld hl, answer ; HL is location of answer to print 37 | jr printAnswer 38 | 39 | hitEscape: 40 | ld hl, escaped ; lHL is ocation of 'escaped' text to print 41 | jr printAnswer 42 | 43 | printAnswer: 44 | SET_BG_COLOUR 0 ; reset background colour to black 45 | TAB_TO 0, 14 ; go to TAB position 46 | 47 | ld bc,0 48 | ld a,0 49 | rst.lil $18 ; print out text stored at HL, terminated in 0 50 | 51 | TAB_TO 0, 16 ; go to TAB position 52 | 53 | ; ------------------ 54 | ; This is where we exit the program 55 | 56 | EXIT_HERE: 57 | 58 | ld a, 17 59 | rst.lil $10 60 | ld a, 128 61 | rst.lil $10 ; BG to black 62 | 63 | pop iy ; Pop all registers back from the stack 64 | pop ix 65 | pop de 66 | pop bc 67 | pop af 68 | ld hl,0 ; Load the MOS API return code (0) for no errors. 69 | ret ; Return to MOS 70 | 71 | ; ------------------ 72 | ; This is the data we send to VDP 73 | 74 | VDUdata: 75 | .db 31, 0, 10 ; TAB to 10,20 76 | .db "Please enter your name:" ; print this text 77 | .db 31, 0, 12 ; TAB to 10,20 78 | .db 17, 128+4 ; background colour 79 | .blkb 10,32 ; a line of 10 spaces 80 | .db 31, 0, 12 ; TAB to 10,20 81 | endVDUdata: 82 | 83 | answer: .db "You typed: " 84 | textBuffer: .blkb 10,0 85 | lineOfSpaces: .blkb 10,32 86 | 87 | escaped: .db "You escaped...",0 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | -------------------------------------------------------------------------------- /udg.asm: -------------------------------------------------------------------------------- 1 | include "myMacros.inc" 2 | 3 | .assume adl=1 ; ez80 ADL memory mode 4 | .org $40000 ; load code here 5 | 6 | jp start_here ; jump to start of code 7 | 8 | .align 64 ; MOS header 9 | .db "MOS",0,1 10 | 11 | start_here: 12 | 13 | push af ; store all the registers 14 | push bc 15 | push de 16 | push ix 17 | push iy 18 | 19 | ; ------------------ 20 | ; This is our actual code 21 | 22 | ; Setup UDG character 23 | 24 | ld hl, udgData ; address of string to use 25 | ld bc, endUgdData - udgData ; length of string 26 | rst.lil $18 ; Call the MOS API to send data to VDP 27 | 28 | ; prepare the screen 29 | 30 | SET_MODE 8 ; mode 8 is 640x480 pixels, 64 colours 31 | SET_COLOUR bright_red ; colours are define at end of this code 32 | TAB_TO 5,10 33 | 34 | ld a, alien 35 | rst.lil $10 ; print our UDG 36 | 37 | SET_COLOUR blue 38 | SET_BG_COLOUR white 39 | TAB_TO 10,15 40 | 41 | ; Sending a VDU byte stream 42 | 43 | ld hl, VDUdata ; address of string to use 44 | ld bc, endVDUdata - VDUdata ; length of string 45 | rst.lil $18 ; Call the MOS API to send data to VDP 46 | 47 | ; reset the colours 48 | 49 | SET_COLOUR bright_white 50 | SET_BG_COLOUR black 51 | 52 | ; ------------------ 53 | ; This is where we exit the program 54 | 55 | pop iy ; Pop all registers back from the stack 56 | pop ix 57 | pop de 58 | pop bc 59 | pop af 60 | ld hl,0 ; Load the MOS API return code (0) for no errors. 61 | ret ; Return to MOS 62 | 63 | ; ------------------ 64 | ; UGD data 65 | alien: equ 128 66 | 67 | udgData: 68 | .db 23, alien ; define UDG character number 69 | .db 10000010b ; binary data 0 70 | .db 01111100b ; binary data 1 71 | .db 10101010b ; binary data 2 72 | .db 10000010b ; binary data 3 73 | .db 11000110b ; binary data 4 74 | .db 00111000b ; binary data 5 75 | .db 01000100b ; binary data 6 76 | .db 10000010b ; binary data 7 77 | 78 | endUgdData: 79 | 80 | ; ------------------ 81 | ; This is the text data we send to VDP 82 | 83 | VDUdata: 84 | .db "Hello Agon UDGs" ; print this text 85 | .db 13,10 ; CR, LF 86 | endVDUdata: 87 | 88 | ; ------------------ 89 | ; colour data 90 | 91 | bright_red: equ 9 92 | green: equ 2 93 | blue: equ 4 94 | white: equ 7 95 | black: equ 0 96 | bright_white: equ 15 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | -------------------------------------------------------------------------------- /usemacros.asm: -------------------------------------------------------------------------------- 1 | include "myMacros.inc" 2 | 3 | .assume adl=1 ; ez80 ADL memory mode 4 | .org $40000 ; load code here 5 | 6 | jp start_here ; jump to start of code 7 | 8 | .align 64 ; MOS header 9 | .db "MOS",0,1 10 | 11 | start_here: 12 | 13 | push af ; store all the registers 14 | push bc 15 | push de 16 | push ix 17 | push iy 18 | 19 | ; ------------------ 20 | ; This is our actual code 21 | 22 | CLS 23 | SET_COLOUR red 24 | SET_BG_COLOUR green 25 | TAB_TO 15,10 26 | 27 | ; Sending a VDU byte stream 28 | 29 | ld hl, VDUdata ; address of string to use 30 | ld bc, endVDUdata - VDUdata ; length of string 31 | rst.lil $18 ; Call the MOS API to send data to VDP 32 | 33 | ; reset the colours 34 | 35 | SET_COLOUR bright_white 36 | SET_BG_COLOUR black 37 | 38 | ; ------------------ 39 | ; This is where we exit the program 40 | 41 | pop iy ; Pop all registers back from the stack 42 | pop ix 43 | pop de 44 | pop bc 45 | pop af 46 | ld hl,0 ; Load the MOS API return code (0) for no errors. 47 | ret ; Return to MOS 48 | 49 | ; ------------------ 50 | ; This is the data we send to VDP 51 | 52 | VDUdata: 53 | .db "Hello Agon macros\r\n" ; print this text 54 | endVDUdata: 55 | 56 | ; ------------------ 57 | ; colour data 58 | 59 | red: equ 1 60 | green: equ 2 61 | blue: equ 4 62 | white: equ 7 63 | black: equ 0 64 | bright_white: equ 15 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | -------------------------------------------------------------------------------- /viewport.asm: -------------------------------------------------------------------------------- 1 | ; extra MACRO files need to go here 2 | include "myMacros.inc" 3 | 4 | .assume adl=1 ; ez80 ADL memory mode 5 | .org $40000 ; load code here 6 | 7 | jp start_here ; jump to start of code 8 | 9 | .align 64 ; MOS header 10 | .db "MOS",0,1 11 | 12 | start_here: 13 | 14 | push af ; store all the registers 15 | push bc 16 | push de 17 | push ix 18 | push iy 19 | 20 | ; ------------------ 21 | ; This is our actual code 22 | 23 | ; prepare the screen 24 | 25 | SET_MODE 8 ; mode 8 is 640x480 pixels, 64 colours 26 | 27 | 28 | ; Sending a VDU byte stream 29 | 30 | ld hl, VDUdata ; address of string to use 31 | ld bc, endVDUdata - VDUdata ; length of string 32 | rst.lil $18 ; Call the MOS API to send data to VDP 33 | 34 | ld a, $08 ; code to send to MOS 35 | rst.lil $08 ; get IX pointer to System Variables 36 | 37 | WAIT_HERE: ; loop here until we hit ESC key 38 | ld a, (ix + $05) ; get ASCII code of key pressed 39 | cp 27 ; check if 27 (ascii code for ESC) 40 | jp z, EXIT_HERE ; if pressed, jump to exit 41 | 42 | jr WAIT_HERE 43 | 44 | ; ------------------ 45 | ; This is where we exit the program 46 | 47 | EXIT_HERE: 48 | 49 | CLS 50 | pop iy ; Pop all registers back from the stack 51 | pop ix 52 | pop de 53 | pop bc 54 | pop af 55 | ld hl,0 ; Load the MOS API return code (0) for no errors. 56 | ret ; Return to MOS 57 | 58 | ; ------------------ 59 | ; This is the data we send to VDP 60 | 61 | VDUdata: 62 | .db 23, 0, 192, 0 ; set to non-scaled graphics 63 | 64 | .db 24 ; set graphics viewport:- 65 | .dw 40, 150, 270, 40 ; 24, left; bottom; right; top; 66 | 67 | ; FOR A SINGLE PIXEL PLOT 68 | 69 | .db 18, 0, bright_red ; set graphics colour: mode (0), colour 70 | 71 | .db 25, 69 ; PLOT: mode (69 is a point in current colour), 72 | .dw 200,80 ; X; Y; 73 | 74 | ; FOR A LINE 75 | 76 | .db 18, 0, bright_magenta ; set graphics colour: mode (0), colour 77 | 78 | .db 25, 69 ; PLOT: mode (69 is a point in current colour), 79 | .dw 300, 60 ; X; Y; 80 | 81 | .db 25, 13 ; PLOT: mode (13 is a line), 82 | .dw 250,130 ; X; Y; 83 | 84 | ; FOR A RECTANGLE 85 | 86 | .db 18, 0, green ; set graphics colour: mode (0), colour 87 | 88 | .db 25, 69 ; PLOT: mode (69 is a point in current colour), 89 | .dw 10,120 ; X; Y; 90 | 91 | .db 25, 101 ; PLOT: mode (101 is a filled rectangle), 92 | .dw 100,180 ; X; Y; 93 | 94 | 95 | ; FOR A CIRCLE 96 | 97 | .db 18, 0, bright_yellow ; set graphics colour: mode (0), colour 98 | 99 | .db 25, 68 ; PLOT: mode (69 is a MOVE TO but don't plot point), 100 | .dw 180,140 ; X; Y; 101 | 102 | .db 25, 149 ; PLOT: mode (149 is an outlined circle), 103 | .dw 200,160 ; X; Y; 104 | 105 | ; FOR A FILLED TRIANGLE 106 | 107 | .db 18, 0, blue ; set graphics colour: mode (0), colour 108 | 109 | .db 25, 69 ; PLOT: mode (69 is a point in current colour), 110 | .dw 10,10 ; X; Y; 111 | 112 | .db 25, 69 ; PLOT: mode (69 is a point in current colour), 113 | .dw 50, 100 ; X; Y; 114 | 115 | .db 25, 85 ; PLOT: mode (85 is a filled triangle), 116 | .dw 200,20 ; X; Y; 117 | 118 | endVDUdata: 119 | 120 | ; ------------------ 121 | ; colour data 122 | 123 | bright_red: equ 9 124 | green: equ 2 125 | bright_yellow: equ 11 126 | bright_magenta: equ 13 127 | blue: equ 4 128 | white: equ 7 129 | black: equ 0 130 | bright_white: equ 15 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | --------------------------------------------------------------------------------