├── .github └── FUNDING.yml ├── .gitignore ├── ASM ├── Hello World 16 │ ├── Hello World 16.wsp │ ├── Hello World 16.zdsproj │ ├── README.md │ ├── eZ80F92_AGON_Flash.ztgt │ ├── equs.inc │ ├── init.asm │ └── main.asm ├── Hello World 24 │ ├── Hello World 24.wsp │ ├── Hello World 24.zdsproj │ ├── README.md │ ├── eZ80F92_AGON_Flash.ztgt │ ├── equs.inc │ ├── init.asm │ └── main.asm └── Memory Dump │ ├── Memory Dump.wsp │ ├── Memory Dump.zdsproj │ ├── README.md │ ├── eZ80F92_AGON_Flash.ztgt │ ├── equs.inc │ ├── init.asm │ ├── macros.inc │ ├── main.asm │ ├── output.asm │ └── parse.asm ├── C ├── Disassembler │ ├── Debug.linkcmd │ ├── Disassembler.wsp │ ├── Disassembler.zdsproj │ ├── README.md │ ├── Release.linkcmd │ ├── eZ80F92_AGON_Flash.ztgt │ ├── init.asm │ └── main.c └── Hello World │ ├── Debug.linkcmd │ ├── Hello World.wsp │ ├── Hello World.zdsproj │ ├── README.md │ ├── Release.linkcmd │ ├── eZ80F92_AGON_Flash.ztgt │ ├── init.asm │ └── main.c ├── LICENSE └── README.md /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | ko_fi: breakintoprogram 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | **/Debug/ 2 | **/Release/ -------------------------------------------------------------------------------- /ASM/Hello World 16/Hello World 16.wsp: -------------------------------------------------------------------------------- 1 | [WorkState_v1_2] 2 | ptn_Child1=Frames 3 | 4 | [WorkState_v1_2.Frames] 5 | ptn_Child1=ChildFrames 6 | 7 | [WorkState_v1_2.Frames.ChildFrames] 8 | 9 | -------------------------------------------------------------------------------- /ASM/Hello World 16/Hello World 16.zdsproj: -------------------------------------------------------------------------------- 1 | 2 | eZ80F92 3 | 4 | 5 | 6 | .\init.asm 7 | .\main.asm 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 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 | 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 | 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 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 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 | 203 | 204 | 205 | 206 | 207 | 208 | -------------------------------------------------------------------------------- /ASM/Hello World 16/README.md: -------------------------------------------------------------------------------- 1 | # Hello World 16 2 | 3 | Usage: `hello_16` 4 | 5 | A simple example of a 16-bit Z80 application in assembler -------------------------------------------------------------------------------- /ASM/Hello World 16/eZ80F92_AGON_Flash.ztgt: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Oscillator 5 | 18432000 6 | 7 | 8 | 000000 9 | C0000 10 | FFFF 11 | true 12 | 13 | 14 | 15 | 0 16 | false 17 | 40000 18 | BFFFF 19 | 20 | 1 21 | false 22 | true 23 | 24 | 25 | 26 | 27 | 1 28 | 8 29 | 2 30 | 9 31 | 32 | 33 | 02 34 | 28 35 | C0 36 | C7 37 | 38 | 39 | 81 40 | 28 41 | 80 42 | BF 43 | 44 | 45 | 02 46 | 00 47 | 00 48 | 00 49 | 50 | 51 | 52 | 0 53 | ff 54 | true 55 | true 56 | 57 | 1 58 | 59 | eZ80F92 60 | 1.0.1 61 | 1.00 62 | 63 | -------------------------------------------------------------------------------- /ASM/Hello World 16/equs.inc: -------------------------------------------------------------------------------- 1 | ; 2 | ; Title: Hello World - Equs 3 | ; Author: Dean Belfield 4 | ; Created: 06/11/2022 5 | ; Last Updated: 06/11/2022 6 | ; 7 | ; Modinfo: 8 | 9 | RAM_Top: EQU 0FF00h 10 | Stack_Top: EQU 00000h ; Stack at top 11 | 12 | ; For GPIO 13 | ; PA not available on eZ80L92 14 | ; 15 | PA_DR: EQU 96h 16 | PA_DDR: EQU 97h 17 | PA_ALT1: EQU 98h 18 | PA_ALT2: EQU 99h 19 | PB_DR: EQU 9Ah 20 | PB_DDR: EQU 9Bh 21 | PB_ALT1: EQU 9Ch 22 | PB_ALT2: EQU 9Dh 23 | PC_DR: EQU 9Eh 24 | PC_DDR: EQU 9Fh 25 | PC_ALT1: EQU A0h 26 | PC_ALT2: EQU A1h 27 | PD_DR: EQU A2h 28 | PD_DDR: EQU A3h 29 | PD_ALT1: EQU A4h 30 | PD_ALT2: EQU A5h 31 | 32 | GPIOMODE_OUT: EQU 0 ; Output 33 | GPIOMODE_IN: EQU 1 ; Input 34 | GPIOMODE_DIO: EQU 2 ; Open Drain IO 35 | GPIOMODE_SIO: EQU 3 ; Open Source IO 36 | GPIOMODE_INTD: EQU 4 ; Interrupt, Dual Edge 37 | GPIOMODE_ALTF: EQU 5; ; Alt Function 38 | GPIOMODE_INTAL: EQU 6 ; Interrupt, Active Low 39 | GPIOMODE_INTAH: EQU 7 ; Interrupt, Active High 40 | GPIOMODE_INTFE: EQU 8 ; Interrupt, Falling Edge 41 | GPIOMODE_INTRE: EQU 9 ; Interrupt, Rising Edge 42 | 43 | ; For serial.asm 44 | ; 45 | BASE_CLOCK EQU 24000000 ; It's actually 48000000 in the Project Settings 46 | 47 | BAUD_500000 EQU BASE_CLOCK / (16 * 500000) 48 | BAUD_250000 EQU BASE_CLOCK / (16 * 250000) 49 | BAUD_125000 EQU BASE_CLOCK / (16 * 125000) 50 | BAUD_19200 EQU BASE_CLOCK / (16 * 19200) 51 | 52 | ; For interrupts.asm 53 | ; 54 | 55 | ;UARTs 56 | ; 57 | UART0_IVECT EQU 18h 58 | UART1_IVECT EQU 1Ah 59 | 60 | ;Ports 61 | ; 62 | PB0_IVECT EQU 30h ; AGON ITRP Interrupt (Pin 28/IO17 of the ESP32) 63 | PB1_IVECT EQU 32h ; AGON VBLANK Interrupt (Pin 23/IO15 of the ESP32) 64 | PB2_IVECT EQU 34h 65 | PB3_IVECT EQU 36h 66 | PB4_IVECT EQU 38h 67 | PB5_IVECT EQU 3Ah 68 | PB6_IVECT EQU 3Ch 69 | PB7_IVECT EQU 3Eh 70 | 71 | PC0_IVECT EQU 40h 72 | PC1_IVECT EQU 42h 73 | PC2_IVECT EQU 44h 74 | PC3_IVECT EQU 46h 75 | PC4_IVECT EQU 48h 76 | PC5_IVECT EQU 4Ah 77 | PC6_IVECT EQU 4Ch 78 | PC7_IVECT EQU 4Eh 79 | 80 | PD0_IVECT EQU 50h 81 | PD1_IVECT EQU 52h 82 | PD2_IVECT EQU 54h 83 | PD3_IVECT EQU 56h 84 | PD4_IVECT EQU 58h 85 | PD5_IVECT EQU 5Ah 86 | PD6_IVECT EQU 5Ch 87 | PD7_IVECT EQU 5Eh 88 | 89 | ; Originally in main.asm 90 | ; 91 | CR: EQU 0DH 92 | LF: EQU 0AH 93 | ESC: EQU 1BH 94 | -------------------------------------------------------------------------------- /ASM/Hello World 16/init.asm: -------------------------------------------------------------------------------- 1 | ; 2 | ; Title: Hello World - Initialisation Code 3 | ; Author: Dean Belfield 4 | ; Created: 06/11/2022 5 | ; Last Updated: 18/12/2022 6 | ; 7 | ; Modinfo: 8 | ; 17/12/2022: Added parameter processing 9 | ; 18/12/2022: SPS now set to 8000h (fix suggested by Reinhard Schu) 10 | 11 | SEGMENT __VECTORS 12 | 13 | XREF _main 14 | 15 | .ASSUME ADL = 0 16 | 17 | INCLUDE "equs.inc" 18 | 19 | argv_ptrs_max: EQU 16 ; Maximum number of arguments allowed in argv 20 | 21 | ; 22 | ; Start in mixed mode. Assumes MBASE is set to correct segment 23 | ; 24 | JP _start ; Jump to start 25 | DS 5 26 | 27 | RST_08: RST.LIS 08h ; API call 28 | RET 29 | DS 5 30 | 31 | RST_10: RST.LIS 10h ; Output 32 | RET 33 | DS 5 34 | 35 | RST_18: DS 8 36 | RST_20: DS 8 37 | RST_28: DS 8 38 | RST_30: DS 8 39 | ; 40 | ; The NMI interrupt vector (not currently used by AGON) 41 | ; 42 | RST_38: EI 43 | RETI 44 | 45 | ; 46 | ; The header stuff is from byte 64 onwards 47 | ; 48 | ALIGN 64 49 | 50 | DB "MOS" ; Flag for MOS - to confirm this is a valid MOS command 51 | DB 00h ; MOS header version 0 52 | DB 00h ; Flag for run mode (0: Z80, 1: ADL) 53 | 54 | SEGMENT CODE 55 | 56 | _exec_name: DB "HELLO.BIN", 0 ; The executable name, only used in argv 57 | 58 | ; 59 | ; And the code follows on immediately after the header 60 | ; 61 | _start: PUSH.LIL IY ; Preserve IY 62 | 63 | LD IY, 0 ; Preserve SPS 64 | ADD IY, SP 65 | PUSH.LIL IY 66 | LD SP, 8000h ; And set to 8000h, top of the MOS command area 67 | 68 | PUSH AF ; Preserve the rest of the registers 69 | PUSH.LIL BC 70 | PUSH.LIL DE 71 | PUSH.LIL IX 72 | 73 | LD A, MB ; Segment base 74 | LD IX, argv_ptrs ; The argv array pointer address 75 | CALL _set_aix24 ; Convert to a 24-bit address 76 | PUSH.LIL IX 77 | CALL _parse_params ; Parse the parameters 78 | POP.LIL IX ; IX: argv 79 | LD B, 0 ; C: argc 80 | CALL _main ; Start user code 81 | 82 | POP.LIL IX ; Restore the registers 83 | POP.LIL DE 84 | POP.LIL BC 85 | POP AF 86 | 87 | POP.LIL IY ; Get the preserved SPS 88 | LD SP, IY ; Restore the SP 89 | 90 | POP.LIL IY ; Restore IY 91 | RET.L ; Return to MOS 92 | 93 | ; Parse the parameter string into a C array 94 | ; Parameters 95 | ; - A: Segment base 96 | ; - HLU: Address of parameter string 97 | ; - IXU: Address for array pointer storage 98 | ; Returns: 99 | ; - C: Number of parameters parsed 100 | ; 101 | _parse_params: LD BC, _exec_name ; Get the address of the app name in this segment 102 | CALL _set_abc24 ; Convert it to a 24-bit address based upon segment base 103 | LD.LIL (IX+0), BC ; ARGV[0] = the executable name 104 | INC.LIL IX 105 | INC.LIL IX 106 | INC.LIL IX 107 | CALL _skip_spaces ; Skip HL past any leading spaces 108 | ; 109 | LD BC, 1 ; C: ARGC = 1 - also clears out top 16 bits of BCU 110 | LD B, argv_ptrs_max - 1 ; B: Maximum number of argv_ptrs 111 | ; 112 | _parse_params_1: PUSH BC ; Stack ARGC 113 | PUSH.LIL HL ; Stack start address of token 114 | CALL _get_token ; Get the next token 115 | LD A, C ; A: Length of the token in characters 116 | POP.LIL DE ; Start address of token (was in HL) 117 | POP BC ; ARGC 118 | OR A ; Check for A=0 (no token found) OR at end of string 119 | RET Z 120 | ; 121 | LD.LIL (IX+0), DE ; Store the pointer to the token 122 | PUSH.LIL HL ; DE=HL 123 | POP.LIL DE 124 | CALL _skip_spaces ; And skip HL past any spaces onto the next character 125 | XOR A 126 | LD.LIL (DE), A ; Zero-terminate the token 127 | INC.LIL IX 128 | INC.LIL IX 129 | INC.LIL IX ; Advance to next pointer position 130 | INC C ; Increment ARGC 131 | LD A, C ; Check for C >= A 132 | CP B 133 | JR C, _parse_params_1 ; And loop 134 | RET 135 | 136 | ; Get the next token 137 | ; Parameters: 138 | ; - HL: Address of parameter string 139 | ; Returns: 140 | ; - HL: Address of first character after token 141 | ; - C: Length of token (in characters) 142 | ; 143 | _get_token: LD C, 0 ; Initialise length 144 | $$: LD.LIL A, (HL) ; Get the character from the parameter string 145 | OR A ; Exit if 0 (end of parameter string in MOS) 146 | RET Z 147 | CP 13 ; Exit if CR (end of parameter string in BBC BASIC) 148 | RET Z 149 | CP ' ' ; Exit if space (end of token) 150 | RET Z 151 | INC.LIL HL ; Advance to next character 152 | INC C ; Increment length 153 | JR $B 154 | 155 | ; Skip spaces in the parameter string 156 | ; Parameters: 157 | ; - HL: Address of parameter string 158 | ; Returns: 159 | ; - HL: Address of next none-space character 160 | ; F: Z if at end of string, otherwise NZ if there are more tokens to be parsed 161 | ; 162 | _skip_spaces: LD.LIL A, (HL) ; Get the character from the parameter string 163 | CP ' ' ; Exit if not space 164 | RET NZ 165 | INC.LIL HL ; Advance to next character 166 | JR _skip_spaces ; Increment length 167 | 168 | ; Set the MSB of BC (U) to A 169 | ; Parameters: 170 | ; - BC: 16-bit address 171 | ; - A: Value to stick in U of BC 172 | ; Returns: 173 | ; - BCU 174 | ; 175 | _set_abc24: PUSH.LIL HL ; Preserve HL 176 | PUSH.LIL BC ; Stick BC onto SPL 177 | LD.LIL HL, 2 ; HL: SP+2 178 | ADD.LIL HL, SP 179 | LD.LIL (HL), A ; Store A in it 180 | POP.LIL BC ; Fetch ammended BC 181 | POP.LIL HL ; Restore HL 182 | RET 183 | 184 | ; Set the MSB of BC (U) to A 185 | ; Parameters: 186 | ; - IX: 16-bit address 187 | ; - A: Value to stick in U of BC 188 | ; Returns: 189 | ; - IXU 190 | ; 191 | _set_aix24: PUSH.LIL IX ; Stick IX onto SPL 192 | LD.LIL IX, 2 ; IX: SP+2 193 | ADD.LIL IX, SP 194 | LD.LIL (IX), A ; Store A in it 195 | POP.LIL IX ; Fetch ammended IX 196 | RET 197 | 198 | ; Storage for the argv array pointers 199 | ; 200 | argv_ptrs: BLKP argv_ptrs_max, 0 ; Storage for the argv array pointers 201 | -------------------------------------------------------------------------------- /ASM/Hello World 16/main.asm: -------------------------------------------------------------------------------- 1 | ; 2 | ; Title: Hello World - Main 3 | ; Author: Dean Belfield 4 | ; Created: 06/11/2022 5 | ; Last Updated: 17/12/2022 6 | ; 7 | ; Modinfo: 8 | ; 17/12/2022: Added parameter processing 9 | 10 | .ASSUME ADL = 0 11 | 12 | INCLUDE "equs.inc" 13 | 14 | SEGMENT CODE 15 | 16 | XDEF _main 17 | 18 | ; The main routine 19 | ; IXU: argv - pointer to array of parameters 20 | ; C: argc - number of parameters 21 | ; Returns: 22 | ; HL: Error code, or 0 if OK 23 | ; 24 | _main: LD HL, s_HELLO_WORLD ; Print "Hello World" 25 | CALL PRSTR16 26 | LD HL, s_ARGUMENTS ; Print "Arguments:" 27 | CALL PRSTR16 28 | 29 | LD B, C ; B: # of arguments 30 | 31 | ; Loop round printing the arguments 32 | ; 33 | $$: LD HL, s_ARGV ; Print " - argv: " 34 | CALL PRSTR16 35 | LD.LIL HL, (IX+0) ; Get the argument string address from the array 36 | CALL PRSTR24 ; And print it 37 | INC.LIL IX ; Increment to the next index in the array 38 | INC.LIL IX 39 | INC.LIL IX 40 | LD HL, s_CRLF ; Print a carriage return 41 | CALL PRSTR16 42 | DJNZ $B ; And loop 43 | 44 | ; Return with error code 0 45 | ; 46 | LD HL, 0 47 | RET 48 | 49 | ; Print a zero-terminated string 50 | ; Parameters: 51 | ; HLU: Address of string (24-bit pointer) 52 | ; 53 | PRSTR24: LD.LIL A, (HL) 54 | OR A 55 | RET Z 56 | RST 10h 57 | INC.LIL HL 58 | JR PRSTR24 59 | 60 | ; Print a zero-terminated string 61 | ; Parameters: 62 | ; HL: Address of string (16-bit pointer) 63 | ; 64 | PRSTR16: LD A,(HL) 65 | OR A 66 | RET Z 67 | RST 10h 68 | INC HL 69 | JR PRSTR16 70 | 71 | ; Sample text 72 | ; 73 | s_HELLO_WORLD: DB "Hello World\n\r", 0 74 | s_ARGUMENTS: DB "Arguments:\n\r", 0 75 | s_ARGV: DB " - argv: ", 0 76 | s_CRLF: DB "\n\r", 0 77 | 78 | ; RAM 79 | ; 80 | DEFINE LORAM, SPACE = ROM 81 | SEGMENT LORAM 82 | -------------------------------------------------------------------------------- /ASM/Hello World 24/Hello World 24.wsp: -------------------------------------------------------------------------------- 1 | [WorkState_v1_2] 2 | ptn_Child1=Frames 3 | 4 | [WorkState_v1_2.Frames] 5 | ptn_Child1=ChildFrames 6 | 7 | [WorkState_v1_2.Frames.ChildFrames] 8 | ptn_Child1=Document-0 9 | ptn_Child2=Document-1 10 | ptn_Child3=Document-2 11 | 12 | [WorkState_v1_2.Frames.ChildFrames.Document-0] 13 | ptn_Child1=ViewFrame-0 14 | 15 | [WorkState_v1_2.Frames.ChildFrames.Document-0.ViewFrame-0] 16 | DocPathName=.\main.asm 17 | DocTemplateIndex=0 18 | DocumentString=IDE.Document 19 | IsActiveChildFrame=False 20 | IsFrameVisible=True 21 | WindowPlacement=MCAAAAAAAAAAAAAABAAAAAAAPPPPPPPPPPPPPPPPMPPPPPPPJOPPPPPPCIAAAAAACIAAAAAAAFFAAAAACFCAAAAA 22 | 23 | [WorkState_v1_2.Frames.ChildFrames.Document-1] 24 | ptn_Child1=ViewFrame-0 25 | 26 | [WorkState_v1_2.Frames.ChildFrames.Document-1.ViewFrame-0] 27 | DocPathName=.\equs.inc 28 | DocTemplateIndex=0 29 | DocumentString=IDE.Document 30 | IsActiveChildFrame=False 31 | IsFrameVisible=True 32 | WindowPlacement=MCAAAAAAAAAAAAAABAAAAAAAPPPPPPPPPPPPPPPPMPPPPPPPJOPPPPPPANAAAAAAANAAAAAAHHDAAAAAAICAAAAA 33 | 34 | [WorkState_v1_2.Frames.ChildFrames.Document-2] 35 | ptn_Child1=ViewFrame-0 36 | 37 | [WorkState_v1_2.Frames.ChildFrames.Document-2.ViewFrame-0] 38 | DocPathName=.\init.asm 39 | DocTemplateIndex=0 40 | DocumentString=IDE.Document 41 | IsActiveChildFrame=True 42 | IsFrameVisible=True 43 | WindowPlacement=MCAAAAAACAAAAAAADAAAAAAAPPPPPPPPPPPPPPPPMPPPPPPPJOPPPPPPEDAAAAAAEDAAAAAALNCAAAAAFOBAAAAA 44 | 45 | -------------------------------------------------------------------------------- /ASM/Hello World 24/Hello World 24.zdsproj: -------------------------------------------------------------------------------- 1 | 2 | eZ80F92 3 | 4 | 5 | 6 | .\init.asm 7 | .\main.asm 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 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 | 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 | 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 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 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 | 203 | 204 | 205 | 206 | -------------------------------------------------------------------------------- /ASM/Hello World 24/README.md: -------------------------------------------------------------------------------- 1 | # Hello World 24 2 | 3 | Usage: `hello_24` 4 | 5 | A simple example of a 24-bit ADL application in assembler -------------------------------------------------------------------------------- /ASM/Hello World 24/eZ80F92_AGON_Flash.ztgt: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Oscillator 5 | 18432000 6 | 7 | 8 | 000000 9 | C0000 10 | FFFF 11 | true 12 | 13 | 14 | 15 | 0 16 | false 17 | 40000 18 | BFFFF 19 | 20 | 1 21 | false 22 | true 23 | 24 | 25 | 26 | 27 | 1 28 | 8 29 | 2 30 | 9 31 | 32 | 33 | 02 34 | 28 35 | C0 36 | C7 37 | 38 | 39 | 81 40 | 28 41 | 80 42 | BF 43 | 44 | 45 | 02 46 | 00 47 | 00 48 | 00 49 | 50 | 51 | 52 | 0 53 | ff 54 | true 55 | true 56 | 57 | 1 58 | 59 | eZ80F92 60 | 1.0.1 61 | 1.00 62 | 63 | -------------------------------------------------------------------------------- /ASM/Hello World 24/equs.inc: -------------------------------------------------------------------------------- 1 | ; 2 | ; Title: Hello World - Equs 3 | ; Author: Dean Belfield 4 | ; Created: 06/11/2022 5 | ; Last Updated: 06/11/2022 6 | ; 7 | ; Modinfo: 8 | 9 | RAM_Top: EQU 0FF00h 10 | Stack_Top: EQU 00000h ; Stack at top 11 | 12 | ; For GPIO 13 | ; PA not available on eZ80L92 14 | ; 15 | PA_DR: EQU 96h 16 | PA_DDR: EQU 97h 17 | PA_ALT1: EQU 98h 18 | PA_ALT2: EQU 99h 19 | PB_DR: EQU 9Ah 20 | PB_DDR: EQU 9Bh 21 | PB_ALT1: EQU 9Ch 22 | PB_ALT2: EQU 9Dh 23 | PC_DR: EQU 9Eh 24 | PC_DDR: EQU 9Fh 25 | PC_ALT1: EQU A0h 26 | PC_ALT2: EQU A1h 27 | PD_DR: EQU A2h 28 | PD_DDR: EQU A3h 29 | PD_ALT1: EQU A4h 30 | PD_ALT2: EQU A5h 31 | 32 | GPIOMODE_OUT: EQU 0 ; Output 33 | GPIOMODE_IN: EQU 1 ; Input 34 | GPIOMODE_DIO: EQU 2 ; Open Drain IO 35 | GPIOMODE_SIO: EQU 3 ; Open Source IO 36 | GPIOMODE_INTD: EQU 4 ; Interrupt, Dual Edge 37 | GPIOMODE_ALTF: EQU 5; ; Alt Function 38 | GPIOMODE_INTAL: EQU 6 ; Interrupt, Active Low 39 | GPIOMODE_INTAH: EQU 7 ; Interrupt, Active High 40 | GPIOMODE_INTFE: EQU 8 ; Interrupt, Falling Edge 41 | GPIOMODE_INTRE: EQU 9 ; Interrupt, Rising Edge 42 | 43 | ; For serial.asm 44 | ; 45 | BASE_CLOCK EQU 24000000 ; It's actually 48000000 in the Project Settings 46 | 47 | BAUD_500000 EQU BASE_CLOCK / (16 * 500000) 48 | BAUD_250000 EQU BASE_CLOCK / (16 * 250000) 49 | BAUD_125000 EQU BASE_CLOCK / (16 * 125000) 50 | BAUD_19200 EQU BASE_CLOCK / (16 * 19200) 51 | 52 | ; For interrupts.asm 53 | ; 54 | 55 | ;UARTs 56 | ; 57 | UART0_IVECT EQU 18h 58 | UART1_IVECT EQU 1Ah 59 | 60 | ;Ports 61 | ; 62 | PB0_IVECT EQU 30h ; AGON ITRP Interrupt (Pin 28/IO17 of the ESP32) 63 | PB1_IVECT EQU 32h ; AGON VBLANK Interrupt (Pin 23/IO15 of the ESP32) 64 | PB2_IVECT EQU 34h 65 | PB3_IVECT EQU 36h 66 | PB4_IVECT EQU 38h 67 | PB5_IVECT EQU 3Ah 68 | PB6_IVECT EQU 3Ch 69 | PB7_IVECT EQU 3Eh 70 | 71 | PC0_IVECT EQU 40h 72 | PC1_IVECT EQU 42h 73 | PC2_IVECT EQU 44h 74 | PC3_IVECT EQU 46h 75 | PC4_IVECT EQU 48h 76 | PC5_IVECT EQU 4Ah 77 | PC6_IVECT EQU 4Ch 78 | PC7_IVECT EQU 4Eh 79 | 80 | PD0_IVECT EQU 50h 81 | PD1_IVECT EQU 52h 82 | PD2_IVECT EQU 54h 83 | PD3_IVECT EQU 56h 84 | PD4_IVECT EQU 58h 85 | PD5_IVECT EQU 5Ah 86 | PD6_IVECT EQU 5Ch 87 | PD7_IVECT EQU 5Eh 88 | 89 | ; Originally in main.asm 90 | ; 91 | CR: EQU 0DH 92 | LF: EQU 0AH 93 | ESC: EQU 1BH 94 | -------------------------------------------------------------------------------- /ASM/Hello World 24/init.asm: -------------------------------------------------------------------------------- 1 | ; 2 | ; Title: Hello World - Initialisation Code 3 | ; Author: Dean Belfield 4 | ; Created: 06/11/2022 5 | ; Last Updated: 17/12/2022 6 | ; 7 | ; Modinfo: 8 | ; 17/12/2022: Added parameter processing 9 | 10 | SEGMENT CODE 11 | 12 | XREF _main 13 | 14 | .ASSUME ADL = 1 15 | 16 | INCLUDE "equs.inc" 17 | 18 | argv_ptrs_max: EQU 16 ; Maximum number of arguments allowed in argv 19 | 20 | ; 21 | ; Start in ADL mode 22 | ; 23 | JP _start ; Jump to start 24 | ; 25 | ; The header stuff is from byte 64 onwards 26 | ; 27 | 28 | _exec_name: DB "HELLO.BIN", 0 ; The executable name, only used in argv 29 | 30 | ALIGN 64 31 | 32 | DB "MOS" ; Flag for MOS - to confirm this is a valid MOS command 33 | DB 00h ; MOS header version 0 34 | DB 01h ; Flag for run mode (0: Z80, 1: ADL) 35 | ; 36 | ; And the code follows on immediately after the header 37 | ; 38 | _start: PUSH AF ; Preserve the registers 39 | PUSH BC 40 | PUSH DE 41 | PUSH IX 42 | PUSH IY 43 | 44 | LD IX, argv_ptrs ; The argv array pointer address 45 | PUSH IX 46 | CALL _parse_params ; Parse the parameters 47 | POP IX ; IX: argv 48 | LD B, 0 ; C: argc 49 | CALL _main ; Start user code 50 | 51 | POP IY ; Restore registers 52 | POP IX 53 | POP DE 54 | POP BC 55 | POP AF 56 | RET 57 | 58 | ; Parse the parameter string into a C array 59 | ; Parameters 60 | ; - HL: Address of parameter string 61 | ; - IX: Address for array pointer storage 62 | ; Returns: 63 | ; - C: Number of parameters parsed 64 | ; 65 | _parse_params: LD BC, _exec_name 66 | LD (IX+0), BC ; ARGV[0] = the executable name 67 | INC IX 68 | INC IX 69 | INC IX 70 | CALL _skip_spaces ; Skip HL past any leading spaces 71 | ; 72 | LD BC, 1 ; C: ARGC = 1 - also clears out top 16 bits of BCU 73 | LD B, argv_ptrs_max - 1 ; B: Maximum number of argv_ptrs 74 | ; 75 | _parse_params_1: 76 | PUSH BC ; Stack ARGC 77 | PUSH HL ; Stack start address of token 78 | CALL _get_token ; Get the next token 79 | LD A, C ; A: Length of the token in characters 80 | POP DE ; Start address of token (was in HL) 81 | POP BC ; ARGC 82 | OR A ; Check for A=0 (no token found) OR at end of string 83 | RET Z 84 | ; 85 | LD (IX+0), DE ; Store the pointer to the token 86 | PUSH HL ; DE=HL 87 | POP DE 88 | CALL _skip_spaces ; And skip HL past any spaces onto the next character 89 | XOR A 90 | LD (DE), A ; Zero-terminate the token 91 | INC IX 92 | INC IX 93 | INC IX ; Advance to next pointer position 94 | INC C ; Increment ARGC 95 | LD A, C ; Check for C >= A 96 | CP B 97 | JR C, _parse_params_1 ; And loop 98 | RET 99 | 100 | ; Get the next token 101 | ; Parameters: 102 | ; - HL: Address of parameter string 103 | ; Returns: 104 | ; - HL: Address of first character after token 105 | ; - C: Length of token (in characters) 106 | ; 107 | _get_token: LD C, 0 ; Initialise length 108 | $$: LD A, (HL) ; Get the character from the parameter string 109 | OR A ; Exit if 0 (end of parameter string in MOS) 110 | RET Z 111 | CP 13 ; Exit if CR (end of parameter string in BBC BASIC) 112 | RET Z 113 | CP ' ' ; Exit if space (end of token) 114 | RET Z 115 | INC HL ; Advance to next character 116 | INC C ; Increment length 117 | JR $B 118 | 119 | ; Skip spaces in the parameter string 120 | ; Parameters: 121 | ; - HL: Address of parameter string 122 | ; Returns: 123 | ; - HL: Address of next none-space character 124 | ; F: Z if at end of string, otherwise NZ if there are more tokens to be parsed 125 | ; 126 | _skip_spaces: LD A, (HL) ; Get the character from the parameter string 127 | CP ' ' ; Exit if not space 128 | RET NZ 129 | INC HL ; Advance to next character 130 | JR _skip_spaces ; Increment length 131 | 132 | ; Storage for the argv array pointers 133 | ; 134 | argv_ptrs: BLKP argv_ptrs_max, 0 -------------------------------------------------------------------------------- /ASM/Hello World 24/main.asm: -------------------------------------------------------------------------------- 1 | ; 2 | ; Title: Hello World - Main 3 | ; Author: Dean Belfield 4 | ; Created: 06/11/2022 5 | ; Last Updated: 17/12/2022 6 | ; 7 | ; Modinfo: 8 | ; 17/12/2022: Added parameter processing 9 | 10 | .ASSUME ADL = 1 11 | 12 | INCLUDE "equs.inc" 13 | 14 | SEGMENT CODE 15 | 16 | XDEF _main 17 | 18 | ; The main routine 19 | ; IX: argv - pointer to array of parameters 20 | ; C: argc - number of parameters 21 | ; Returns: 22 | ; HL: Error code, or 0 if OK 23 | ; 24 | _main: LD HL, s_HELLO_WORLD ; Print "Hello World" 25 | CALL PRSTR 26 | LD HL, s_ARGUMENTS ; Print "Arguments:" 27 | CALL PRSTR 28 | 29 | LD B, C ; B: # of arguments 30 | 31 | ; Loop round printing the arguments 32 | ; 33 | $$: LD HL, s_ARGV ; Print " - argv: " 34 | CALL PRSTR 35 | LD HL, (IX+0) ; Get the argument string address from the array 36 | CALL PRSTR ; And print it 37 | INC IX ; Increment to the next index in the array 38 | INC IX 39 | INC IX 40 | LD HL, s_CRLF ; Print a carriage return 41 | CALL PRSTR 42 | DJNZ $B ; And loop 43 | 44 | ; Return with error code 0 45 | ; 46 | LD HL, 0 47 | RET 48 | 49 | ; Print a zero-terminated string 50 | ; 51 | PRSTR: LD A,(HL) 52 | OR A 53 | RET Z 54 | RST.LIL 10h 55 | INC HL 56 | JR PRSTR 57 | 58 | ; Sample text 59 | ; 60 | s_HELLO_WORLD: DB "Hello World\n\r", 0 61 | s_ARGUMENTS: DB "Arguments:\n\r", 0 62 | s_ARGV: DB " - argv: ", 0 63 | s_CRLF: DB "\n\r", 0 64 | 65 | ; RAM 66 | ; 67 | DEFINE LORAM, SPACE = ROM 68 | SEGMENT LORAM 69 | -------------------------------------------------------------------------------- /ASM/Memory Dump/Memory Dump.wsp: -------------------------------------------------------------------------------- 1 | [WorkState_v1_2] 2 | ptn_Child1=Frames 3 | 4 | [WorkState_v1_2.Frames] 5 | ptn_Child1=ChildFrames 6 | 7 | [WorkState_v1_2.Frames.ChildFrames] 8 | 9 | -------------------------------------------------------------------------------- /ASM/Memory Dump/Memory Dump.zdsproj: -------------------------------------------------------------------------------- 1 | 2 | eZ80F92 3 | 4 | 5 | 6 | .\init.asm 7 | .\main.asm 8 | .\parse.asm 9 | .\output.asm 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 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 | 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 | 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 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 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 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | -------------------------------------------------------------------------------- /ASM/Memory Dump/README.md: -------------------------------------------------------------------------------- 1 | # memdump 2 | 3 | Usage: `memdump start_address (number of bytes)` 4 | 5 | A simple memory dump to screen utility written as a 16-bit Z80 application 6 | 7 | - Start address and number of bytes can be specified in decimal, or hexadecimal if prefixed with an `&` 8 | - Number of bytes is optional, and defaults to 256 -------------------------------------------------------------------------------- /ASM/Memory Dump/eZ80F92_AGON_Flash.ztgt: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Oscillator 5 | 18432000 6 | 7 | 8 | 000000 9 | C0000 10 | FFFF 11 | true 12 | 13 | 14 | 15 | 0 16 | false 17 | 40000 18 | BFFFF 19 | 20 | 1 21 | false 22 | true 23 | 24 | 25 | 26 | 27 | 1 28 | 8 29 | 2 30 | 9 31 | 32 | 33 | 02 34 | 28 35 | C0 36 | C7 37 | 38 | 39 | 81 40 | 28 41 | 80 42 | BF 43 | 44 | 45 | 02 46 | 00 47 | 00 48 | 00 49 | 50 | 51 | 52 | 0 53 | ff 54 | true 55 | true 56 | 57 | 1 58 | 59 | eZ80F92 60 | 1.0.1 61 | 1.00 62 | 63 | -------------------------------------------------------------------------------- /ASM/Memory Dump/equs.inc: -------------------------------------------------------------------------------- 1 | ; 2 | ; Title: Memory Dump - Equs 3 | ; Author: Dean Belfield 4 | ; Created: 15/11/2022 5 | ; Last Updated: 15/11/2022 6 | ; 7 | ; Modinfo: 8 | 9 | RAM_Top: EQU 0FF00h 10 | Stack_Top: EQU 00000h ; Stack at top 11 | 12 | ; For GPIO 13 | ; PA not available on eZ80L92 14 | ; 15 | PA_DR: EQU 96h 16 | PA_DDR: EQU 97h 17 | PA_ALT1: EQU 98h 18 | PA_ALT2: EQU 99h 19 | PB_DR: EQU 9Ah 20 | PB_DDR: EQU 9Bh 21 | PB_ALT1: EQU 9Ch 22 | PB_ALT2: EQU 9Dh 23 | PC_DR: EQU 9Eh 24 | PC_DDR: EQU 9Fh 25 | PC_ALT1: EQU A0h 26 | PC_ALT2: EQU A1h 27 | PD_DR: EQU A2h 28 | PD_DDR: EQU A3h 29 | PD_ALT1: EQU A4h 30 | PD_ALT2: EQU A5h 31 | 32 | GPIOMODE_OUT: EQU 0 ; Output 33 | GPIOMODE_IN: EQU 1 ; Input 34 | GPIOMODE_DIO: EQU 2 ; Open Drain IO 35 | GPIOMODE_SIO: EQU 3 ; Open Source IO 36 | GPIOMODE_INTD: EQU 4 ; Interrupt, Dual Edge 37 | GPIOMODE_ALTF: EQU 5; ; Alt Function 38 | GPIOMODE_INTAL: EQU 6 ; Interrupt, Active Low 39 | GPIOMODE_INTAH: EQU 7 ; Interrupt, Active High 40 | GPIOMODE_INTFE: EQU 8 ; Interrupt, Falling Edge 41 | GPIOMODE_INTRE: EQU 9 ; Interrupt, Rising Edge 42 | 43 | ; For serial.asm 44 | ; 45 | BASE_CLOCK EQU 24000000 ; It's actually 48000000 in the Project Settings 46 | 47 | BAUD_500000 EQU BASE_CLOCK / (16 * 500000) 48 | BAUD_250000 EQU BASE_CLOCK / (16 * 250000) 49 | BAUD_125000 EQU BASE_CLOCK / (16 * 125000) 50 | BAUD_19200 EQU BASE_CLOCK / (16 * 19200) 51 | 52 | ; For interrupts.asm 53 | ; 54 | 55 | ;UARTs 56 | ; 57 | UART0_IVECT EQU 18h 58 | UART1_IVECT EQU 1Ah 59 | 60 | ;Ports 61 | ; 62 | PB0_IVECT EQU 30h ; AGON ITRP Interrupt (Pin 28/IO17 of the ESP32) 63 | PB1_IVECT EQU 32h ; AGON VBLANK Interrupt (Pin 23/IO15 of the ESP32) 64 | PB2_IVECT EQU 34h 65 | PB3_IVECT EQU 36h 66 | PB4_IVECT EQU 38h 67 | PB5_IVECT EQU 3Ah 68 | PB6_IVECT EQU 3Ch 69 | PB7_IVECT EQU 3Eh 70 | 71 | PC0_IVECT EQU 40h 72 | PC1_IVECT EQU 42h 73 | PC2_IVECT EQU 44h 74 | PC3_IVECT EQU 46h 75 | PC4_IVECT EQU 48h 76 | PC5_IVECT EQU 4Ah 77 | PC6_IVECT EQU 4Ch 78 | PC7_IVECT EQU 4Eh 79 | 80 | PD0_IVECT EQU 50h 81 | PD1_IVECT EQU 52h 82 | PD2_IVECT EQU 54h 83 | PD3_IVECT EQU 56h 84 | PD4_IVECT EQU 58h 85 | PD5_IVECT EQU 5Ah 86 | PD6_IVECT EQU 5Ch 87 | PD7_IVECT EQU 5Eh 88 | 89 | ; Originally in main.asm 90 | ; 91 | CR: EQU 0DH 92 | LF: EQU 0AH 93 | ESC: EQU 1BH 94 | -------------------------------------------------------------------------------- /ASM/Memory Dump/init.asm: -------------------------------------------------------------------------------- 1 | ; 2 | ; Title: Memory Dump - Initialisation Code 3 | ; Author: Dean Belfield 4 | ; Created: 15/11/2022 5 | ; Last Updated: 23/12/2022 6 | ; 7 | ; Modinfo: 8 | ; 23/12/2022: Added parameter parsing code 9 | 10 | SEGMENT __VECTORS 11 | 12 | XREF _main 13 | 14 | .ASSUME ADL = 0 15 | 16 | INCLUDE "equs.inc" 17 | 18 | argv_ptrs_max: EQU 16 ; Maximum number of arguments allowed in argv 19 | 20 | ; 21 | ; Start in mixed mode. Assumes MBASE is set to correct segment 22 | ; 23 | JP _start ; Jump to start 24 | DS 5 25 | 26 | RST_08: RST.LIS 08h ; API call 27 | RET 28 | DS 5 29 | 30 | RST_10: RST.LIS 10h ; Output 31 | RET 32 | DS 5 33 | 34 | RST_18: DS 8 35 | RST_20: DS 8 36 | RST_28: DS 8 37 | RST_30: DS 8 38 | ; 39 | ; The NMI interrupt vector (not currently used by AGON) 40 | ; 41 | RST_38: EI 42 | RETI 43 | ; 44 | ; The header stuff is from byte 64 onwards 45 | ; 46 | ALIGN 64 47 | 48 | DB "MOS" ; Flag for MOS - to confirm this is a valid MOS command 49 | DB 00h ; MOS header version 0 50 | DB 00h ; Flag for run mode (0: Z80, 1: ADL) 51 | 52 | _exec_name: DB "MEMDUMP.BIN", 0 ; The executable name, only used in argv 53 | 54 | ; 55 | ; And the code follows on immediately after the header 56 | ; 57 | _start: PUSH.LIL IY ; Preserve IY 58 | 59 | LD IY, 0 ; Preserve SPS 60 | ADD IY, SP 61 | PUSH.LIL IY 62 | LD SP, 8000h ; And set to 8000h, top of the MOS command area 63 | 64 | PUSH AF ; Preserve the rest of the registers 65 | PUSH.LIL BC 66 | PUSH.LIL DE 67 | PUSH.LIL IX 68 | 69 | LD A, MB ; Segment base 70 | LD IX, argv_ptrs ; The argv array pointer address 71 | CALL _set_aix24 ; Convert to a 24-bit address 72 | PUSH.LIL IX 73 | CALL _parse_params ; Parse the parameters 74 | POP.LIL IX ; IX: argv 75 | LD B, 0 ; C: argc 76 | CALL _main ; Start user code 77 | 78 | POP.LIL IX ; Restore the registers 79 | POP.LIL DE 80 | POP.LIL BC 81 | POP AF 82 | 83 | POP.LIL IY ; Get the preserved SPS 84 | LD SP, IY ; Restore the SP 85 | 86 | POP.LIL IY ; Restore IY 87 | RET.L ; Return to MOS 88 | 89 | ; Parse the parameter string into a C array 90 | ; Parameters 91 | ; - A: Segment base 92 | ; - HLU: Address of parameter string 93 | ; - IXU: Address for array pointer storage 94 | ; Returns: 95 | ; - C: Number of parameters parsed 96 | ; 97 | _parse_params: LD BC, _exec_name ; Get the address of the app name in this segment 98 | CALL _set_abc24 ; Convert it to a 24-bit address based upon segment base 99 | LD.LIL (IX+0), BC ; ARGV[0] = the executable name 100 | INC.LIL IX 101 | INC.LIL IX 102 | INC.LIL IX 103 | CALL _skip_spaces ; Skip HL past any leading spaces 104 | ; 105 | LD BC, 1 ; C: ARGC = 1 - also clears out top 16 bits of BCU 106 | LD B, argv_ptrs_max - 1 ; B: Maximum number of argv_ptrs 107 | ; 108 | _parse_params_1: PUSH BC ; Stack ARGC 109 | PUSH.LIL HL ; Stack start address of token 110 | CALL _get_token ; Get the next token 111 | LD A, C ; A: Length of the token in characters 112 | POP.LIL DE ; Start address of token (was in HL) 113 | POP BC ; ARGC 114 | OR A ; Check for A=0 (no token found) OR at end of string 115 | RET Z 116 | ; 117 | LD.LIL (IX+0), DE ; Store the pointer to the token 118 | PUSH.LIL HL ; DE=HL 119 | POP.LIL DE 120 | CALL _skip_spaces ; And skip HL past any spaces onto the next character 121 | XOR A 122 | LD.LIL (DE), A ; Zero-terminate the token 123 | INC.LIL IX 124 | INC.LIL IX 125 | INC.LIL IX ; Advance to next pointer position 126 | INC C ; Increment ARGC 127 | LD A, C ; Check for C >= A 128 | CP B 129 | JR C, _parse_params_1 ; And loop 130 | RET 131 | 132 | ; Get the next token 133 | ; Parameters: 134 | ; - HL: Address of parameter string 135 | ; Returns: 136 | ; - HL: Address of first character after token 137 | ; - C: Length of token (in characters) 138 | ; 139 | _get_token: LD C, 0 ; Initialise length 140 | $$: LD.LIL A, (HL) ; Get the character from the parameter string 141 | OR A ; Exit if 0 (end of parameter string in MOS) 142 | RET Z 143 | CP 13 ; Exit if CR (end of parameter string in BBC BASIC) 144 | RET Z 145 | CP ' ' ; Exit if space (end of token) 146 | RET Z 147 | INC.LIL HL ; Advance to next character 148 | INC C ; Increment length 149 | JR $B 150 | 151 | ; Skip spaces in the parameter string 152 | ; Parameters: 153 | ; - HL: Address of parameter string 154 | ; Returns: 155 | ; - HL: Address of next none-space character 156 | ; F: Z if at end of string, otherwise NZ if there are more tokens to be parsed 157 | ; 158 | _skip_spaces: LD.LIL A, (HL) ; Get the character from the parameter string 159 | CP ' ' ; Exit if not space 160 | RET NZ 161 | INC.LIL HL ; Advance to next character 162 | JR _skip_spaces ; Increment length 163 | 164 | ; Set the MSB of BC (U) to A 165 | ; Parameters: 166 | ; - BC: 16-bit address 167 | ; - A: Value to stick in U of BC 168 | ; Returns: 169 | ; - BCU 170 | ; 171 | _set_abc24: PUSH.LIL HL ; Preserve HL 172 | PUSH.LIL BC ; Stick BC onto SPL 173 | LD.LIL HL, 2 ; HL: SP+2 174 | ADD.LIL HL, SP 175 | LD.LIL (HL), A ; Store A in it 176 | POP.LIL BC ; Fetch ammended BC 177 | POP.LIL HL ; Restore HL 178 | RET 179 | 180 | ; Set the MSB of BC (U) to A 181 | ; Parameters: 182 | ; - IX: 16-bit address 183 | ; - A: Value to stick in U of BC 184 | ; Returns: 185 | ; - IXU 186 | ; 187 | _set_aix24: PUSH.LIL IX ; Stick IX onto SPL 188 | LD.LIL IX, 2 ; IX: SP+2 189 | ADD.LIL IX, SP 190 | LD.LIL (IX), A ; Store A in it 191 | POP.LIL IX ; Fetch ammended IX 192 | RET 193 | 194 | ; Storage for the argv array pointers 195 | ; 196 | argv_ptrs: BLKP argv_ptrs_max, 0 ; Storage for the argv array pointers 197 | 198 | END -------------------------------------------------------------------------------- /ASM/Memory Dump/macros.inc: -------------------------------------------------------------------------------- 1 | ; 2 | ; Title: Memory Dump - Macros 3 | ; Author: Dean Belfield 4 | ; Created: 15/11/2022 5 | ; Last Updated: 15/11/2022 6 | ; 7 | ; Modinfo: 8 | 9 | ADD8U_HL: MACRO reg 10 | ADD A, L 11 | LD L, A 12 | ADC A, H 13 | SUB L 14 | LD H, A 15 | ENDMACRO 16 | 17 | ADD8U_DE: MACRO reg 18 | ADD A, E 19 | LD E, A 20 | ADC A, D 21 | SUB E 22 | LD D, A 23 | ENDMACRO -------------------------------------------------------------------------------- /ASM/Memory Dump/main.asm: -------------------------------------------------------------------------------- 1 | ; 2 | ; Title: Memory Dump - Main 3 | ; Author: Dean Belfield 4 | ; Created: 15/11/2022 5 | ; Last Updated: 23/03/2023 6 | ; 7 | ; Modinfo: 8 | ; 23/12/2022: Added parameter parsing code, help text 9 | ; 23/03/2023: Fixed to work with MOS 1.03 10 | 11 | .ASSUME ADL = 0 12 | 13 | INCLUDE "equs.inc" 14 | INCLUDE "mos_api.inc" ; In MOS/src 15 | 16 | SEGMENT CODE 17 | 18 | XDEF _main 19 | 20 | XREF ASC_TO_NUMBER 21 | 22 | XREF Print_String 23 | XREF Print_Hex24 24 | XREF Print_Hex16 25 | XREF Print_Hex8 26 | 27 | ; Error: Invalid parameter 28 | ; 29 | _err_invalid_param: LD HL, 19 ; The return code: Invalid parameters 30 | RET 31 | 32 | ; Help text 33 | ; 34 | _help: LD HL, _help_text 35 | CALL Print_String 36 | LD HL, 0 ; The return code: OK 37 | RET 38 | 39 | _help_text: DB "AGON Memory Dump by Dean Belfield\n\r" 40 | DB "Usage:\n\r"; 41 | DB "memdump address \n\r", 0; 42 | 43 | ; The main routine 44 | ; IXU: argv - pointer to array of parameters 45 | ; C: argc - number of parameters 46 | ; Returns: 47 | ; HL: Error code, or 0 if OK 48 | ; 49 | _main: LD DE, 100h ; Default number of bytes to fetch 50 | LD A, C ; Fetch number of parameters 51 | CP 2 ; Is it less than 2? 52 | JR C, _help ; Then goto help 53 | JR Z, $F ; If it is equal to 2, then proceed with default number of bytes to fetch 54 | CP 4 ; Is it greater than or equal to 4? 55 | JR NC, _help ; Yes, so goto help 56 | ; 57 | LD.LIL HL,(IX+6) ; HLU: Pointer to the length parameter string 58 | CALL ASC_TO_NUMBER ; DEU: length 59 | ; 60 | $$: PUSH.LIL DE ; Stack the length 61 | LD.LIL HL,(IX+3) ; HLU: Pointer to the start address parameter string 62 | CALL ASC_TO_NUMBER ; DEU: Start address 63 | DB 5Bh ; Prefix for EX.L DE, HL (bodge- cannot get Zilog tools to compile this!) 64 | EX DE, HL 65 | ; 66 | POP.LIL DE ; Restore the length 67 | CALL Memory_Dump 68 | ; 69 | LD HL, 0 ; Return with OK 70 | RET 71 | 72 | ; Memory Dump 73 | ; HLU: Start of memory to dump 74 | ; DE: Number of bytes to dump out 75 | ; 76 | Memory_Dump: CALL Print_Hex24 77 | LD A, ':' 78 | RST 10h 79 | LD A, ' ' 80 | RST 10h 81 | LD B, 16 82 | LD IX, Buffer 83 | LD (IX+0), ' ' 84 | ; 85 | Memory_Dump_1: LD.LIL A, (HL) 86 | PUSH AF 87 | CP 07Fh ; DEL is non-printable 88 | JR Z, Memory_Dump_1a 89 | CP ' ' ; As are all control chars. 90 | JR NC, Memory_Dump_2 91 | Memory_Dump_1a: LD A, '.' ; replace nonprintable chars with dot. 92 | Memory_Dump_2: LD (IX+1), A 93 | INC IX 94 | POP AF 95 | CALL Print_Hex8 96 | INC.LIL HL 97 | DEC DE 98 | LD A, D 99 | OR E 100 | JR Z, Memory_Dump_3 101 | DJNZ Memory_Dump_1 102 | CALL Memory_Dump_5 103 | PUSH IX ; Check for ESC 104 | MOSCALL mos_sysvars ; IX: Address of system variables 105 | LD.LIL A, (IX + sysvar_keyascii) 106 | POP IX 107 | CP 1Bh 108 | JR NZ, Memory_Dump 109 | RET 110 | 111 | Memory_Dump_3: LD A, B 112 | OR A 113 | JR Z, Memory_Dump_5 114 | DEC B 115 | JR Z, Memory_Dump_5 116 | LD A, ' ' 117 | ; 118 | Memory_Dump_4: RST 10h 119 | RST 10h 120 | DJNZ Memory_Dump_4 121 | ; 122 | Memory_Dump_5: LD (IX+1),0Dh 123 | LD (IX+2),0Ah 124 | LD (IX+3),00h 125 | PUSH.LIL HL 126 | LD HL, Buffer 127 | CALL Print_String 128 | POP.LIL HL 129 | RET 130 | 131 | ; RAM 132 | ; 133 | DEFINE LORAM, SPACE = ROM 134 | SEGMENT LORAM 135 | 136 | Buffer: DS 256 137 | -------------------------------------------------------------------------------- /ASM/Memory Dump/output.asm: -------------------------------------------------------------------------------- 1 | ; 2 | ; Title: Memory Dump - Output functions 3 | ; Author: Dean Belfield 4 | ; Created: 15/11/2022 5 | ; Last Updated: 15/11/2022 6 | ; 7 | ; Modinfo: 8 | 9 | INCLUDE "equs.inc" 10 | 11 | .ASSUME ADL = 0 12 | 13 | SEGMENT CODE 14 | 15 | XDEF Print_String 16 | XDEF Print_Hex24 17 | XDEF Print_Hex16 18 | XDEF Print_Hex8 19 | 20 | ; Print a zero-terminated string 21 | ; 22 | Print_String: LD A,(HL) 23 | OR A 24 | RET Z 25 | RST 10h 26 | INC HL 27 | JR Print_String 28 | 29 | ; Print a 24-bit HEX number 30 | ; HLU: Number to print 31 | ; 32 | Print_Hex24: PUSH.LIL HL 33 | LD.LIL HL, 2 34 | ADD.LIL HL, SP 35 | LD.LIL A, (HL) 36 | POP.LIL HL 37 | 38 | CALL Print_Hex8 39 | 40 | ; Print a 16-bit HEX number 41 | ; HL: Number to print 42 | ; 43 | Print_Hex16: LD A,H 44 | CALL Print_Hex8 45 | LD A,L 46 | 47 | ; Print an 8-bit HEX number 48 | ; A: Number to print 49 | ; 50 | Print_Hex8: LD C,A 51 | RRA 52 | RRA 53 | RRA 54 | RRA 55 | CALL $F 56 | LD A,C 57 | $$: AND 0Fh 58 | ADD A,90h 59 | DAA 60 | ADC A,40h 61 | DAA 62 | RST 10h 63 | RET -------------------------------------------------------------------------------- /ASM/Memory Dump/parse.asm: -------------------------------------------------------------------------------- 1 | ; 2 | ; Title: Memory Dump - Parsing Functions 3 | ; Author: Dean Belfield 4 | ; Created: 15/11/2022 5 | ; Last Updated: 15/11/2022 6 | ; 7 | ; Modinfo: 8 | 9 | INCLUDE "equs.inc" 10 | INCLUDE "macros.inc" 11 | 12 | .ASSUME ADL = 0 13 | 14 | SEGMENT CODE 15 | 16 | XDEF ASC_TO_NUMBER 17 | XDEF SKIPSP 18 | XDEF UPPRC 19 | 20 | ; Read a number and convert to binary 21 | ; If prefixed with &, will read as hex, otherwise decimal 22 | ; Inputs: HL: Pointer in string buffer 23 | ; Outputs: HL: Updated text pointer 24 | ; DE: Value 25 | ; A: Terminator (spaces skipped) 26 | ; F: Carry set if valid number, otherwise reset 27 | ; Destroys: A,D,E,H,L,F 28 | ; 29 | ASC_TO_NUMBER: LD.LIL DE, 0 ; Initialise DE 30 | CALL SKIPSP ; Skip whitespace 31 | LD.LIL A, (HL) ; Read first character 32 | OR A ; Check for end of string 33 | RET Z ; Return with no carry if not 34 | PUSH.LIL BC ; Preserve BC 35 | CP '&' ; Is it prefixed with '&' (HEX number)? 36 | JR NZ, ASC_TO_NUMBER3 ; Jump to decimal parser if not 37 | INC.LIL HL ; Otherwise fall through to ASC_TO_HEX 38 | ; 39 | ASC_TO_NUMBER1: LD.LIL A, (HL) ; Fetch the character 40 | CALL UPPRC ; Convert to uppercase 41 | SUB '0' ; Normalise to 0 42 | JR C, ASC_TO_NUMBER4 ; Return if < ASCII '0' 43 | CP 10 ; Check if >= 10 44 | JR C, ASC_TO_NUMBER2 ; No, so skip next bit 45 | SUB 7 ; Adjust ASCII A-F to nibble 46 | CP 16 ; Check for > F 47 | JR NC, ASC_TO_NUMBER4 ; Return if out of range 48 | ; 49 | ASC_TO_NUMBER2: PUSH.LIL HL ; Stack HL 50 | PUSH.LIL DE ; LD HL, DE 51 | POP.LIL HL 52 | ADD.LIL HL, HL 53 | ADD.LIL HL, HL 54 | ADD.LIL HL, HL 55 | ADD.LIL HL, HL 56 | PUSH.LIL HL ; LD DE, HL 57 | POP.LIL DE 58 | POP.LIL HL ; Restore HL 59 | OR E ; OR the new digit in to the least significant nibble 60 | LD E, A 61 | ; 62 | INC.LIL HL ; Onto the next character 63 | JR ASC_TO_NUMBER1 ; And loop 64 | ; 65 | ASC_TO_NUMBER3: LD.LIL A, (HL) 66 | SUB '0' ; Normalise to 0 67 | JR C, ASC_TO_NUMBER4 ; Return if < ASCII '0' 68 | CP 10 ; Check if >= 10 69 | JR NC, ASC_TO_NUMBER4 ; Return if >= 10 70 | ; 71 | PUSH.LIL HL ; Stack HL 72 | PUSH.LIL DE ; LD HL, DE 73 | POP.LIL HL 74 | PUSH.LIL HL ; LD BC, HL 75 | POP.LIL BC 76 | ADD.LIL HL, HL ; x 2 77 | ADD.LIL HL, HL ; x 4 78 | ADD.LIL HL, BC ; x 5 79 | ADD.LIL HL, HL ; x 10 80 | LD.LIL BC, 0 81 | LD C, A ; LD BCU, A 82 | ADD.LIL HL, BC ; Add BCU to HL 83 | PUSH.LIL HL ; LD DE, HL 84 | POP.LIL DE 85 | POP.LIL HL ; Restore HL 86 | ; 87 | INC.LIL HL 88 | JR ASC_TO_NUMBER3 89 | ASC_TO_NUMBER4: POP.LIL BC 90 | SCF ; We have a valid number so set carry 91 | RET 92 | 93 | ; Skip a space 94 | ; HL: Pointer in string buffer 95 | ; 96 | SKIPSP: LD.LIL A, (HL) 97 | CP ' ' 98 | RET NZ 99 | INC.LIL HL 100 | JR SKIPSP 101 | 102 | ; Convert a character to upper case 103 | ; A: Character to convert 104 | ; 105 | UPPRC: AND 7FH 106 | CP '`' 107 | RET C 108 | AND 5FH ; Convert to upper case 109 | RET -------------------------------------------------------------------------------- /C/Disassembler/Debug.linkcmd: -------------------------------------------------------------------------------- 1 | -FORMAT=OMF695,INTEL32 2 | -map -maxhexlen=64 -quiet -warnoverlap -xref -unresolved=fatal 3 | -sort NAME=ascending -warn -debug -NOigcase 4 | 5 | ; SEARCHPATH="C:\Tools\ZiLOG\ZDSII_eZ80Acclaim!_5.3.4\lib" 6 | 7 | RANGE ROM $000000 : $01FFFF 8 | RANGE RAM $040000 : $0BFFFF 9 | RANGE EXTIO $000000 : $00FFFF 10 | RANGE INTIO $000000 : $0000FF 11 | 12 | CHANGE CODE = RAM 13 | CHANGE STRSECT = RAM 14 | CHANGE DATA = RAM 15 | 16 | ORDER CODE,DATA 17 | 18 | DEFINE __low_bss = base of BSS 19 | DEFINE __len_bss = length of BSS 20 | 21 | "Disassembler"= \ 22 | ".\init.obj", \ 23 | ".\main.obj", \ 24 | "C:\Tools\ZiLOG\ZDSII_eZ80Acclaim!_5.3.4\lib\std\chelpD.lib", \ 25 | "C:\Tools\ZiLOG\ZDSII_eZ80Acclaim!_5.3.4\lib\std\crtD.lib", \ 26 | "C:\Tools\ZiLOG\ZDSII_eZ80Acclaim!_5.3.4\lib\std\crtSD.lib", \ 27 | "C:\Tools\ZiLOG\ZDSII_eZ80Acclaim!_5.3.4\lib\std\nokernelD.lib", \ 28 | "C:\Tools\ZiLOG\ZDSII_eZ80Acclaim!_5.3.4\lib\zilog\zsldevinitdummy.obj" 29 | 30 | -------------------------------------------------------------------------------- /C/Disassembler/Disassembler.wsp: -------------------------------------------------------------------------------- 1 | [WorkState_v1_2] 2 | ptn_Child1=Frames 3 | 4 | [WorkState_v1_2.Frames] 5 | ptn_Child1=ChildFrames 6 | 7 | [WorkState_v1_2.Frames.ChildFrames] 8 | 9 | -------------------------------------------------------------------------------- /C/Disassembler/Disassembler.zdsproj: -------------------------------------------------------------------------------- 1 | 2 | eZ80F92 3 | 4 | 5 | 6 | .\init.asm 7 | .\main.c 8 | .\Debug.linkcmd 9 | .\Release.linkcmd 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 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 | 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 | 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 | 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 | 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 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | -------------------------------------------------------------------------------- /C/Disassembler/README.md: -------------------------------------------------------------------------------- 1 | # Disassembler 2 | 3 | Usage: `disassemble ` 4 | 5 | An eZ80 disassembler for MOS 6 | 7 | ### Parameters 8 | 9 | Parameters can be specified in hexadecimal or decimal. Prefix the number with an & for hexadecimal. 10 | 11 | - `start address`: Where to start the disassembly from 12 | - `length`: Number of bytes to disassemble 13 | - `adl mode`: Disassembly mode (optional, defaults to 1) 14 | 15 | ### ADL mode 16 | 17 | When set to 1 it will default to disassembling 24-bit Z80 code. If set to 0, it will disassemble within a 64K segment, and will display any address references relative to that segment. 18 | 19 | ### Display 20 | 21 | The disassembly is spread across 4 columns: 22 | 23 | - Address 24 | - Opcode bytes: The instruction in hexadecimal 25 | - Opcode chars: The opcode bytes displayed in ASCII, handy to check if you are disassembling data 26 | - Mnemonics The disassembled instruction 27 | 28 | All values are displayed in hexadecimal 29 | 30 | ### Compiling 31 | 32 | - The paths in the link files (Debug.linkcmd and Release.linkcmd) need to be modified to reflect where the tools are located on your hard drive before this will compile. -------------------------------------------------------------------------------- /C/Disassembler/Release.linkcmd: -------------------------------------------------------------------------------- 1 | -FORMAT=OMF695,INTEL32 2 | -map -maxhexlen=64 -quiet -warnoverlap -xref -unresolved=fatal 3 | -sort NAME=ascending -warn -debug -NOigcase 4 | 5 | ; SEARCHPATH="C:\Tools\ZiLOG\ZDSII_eZ80Acclaim!_5.3.4\lib" 6 | 7 | RANGE ROM $000000 : $01FFFF 8 | RANGE RAM $0B0000 : $0BFFFF 9 | RANGE EXTIO $000000 : $00FFFF 10 | RANGE INTIO $000000 : $0000FF 11 | 12 | CHANGE CODE = RAM 13 | CHANGE STRSECT = RAM 14 | CHANGE DATA = RAM 15 | 16 | ORDER CODE,DATA 17 | 18 | DEFINE __low_bss = base of BSS 19 | DEFINE __len_bss = length of BSS 20 | 21 | "Disassembler"= \ 22 | ".\init.obj", \ 23 | ".\main.obj", \ 24 | "C:\Tools\ZiLOG\ZDSII_eZ80Acclaim!_5.3.4\lib\std\chelp.lib", \ 25 | "C:\Tools\ZiLOG\ZDSII_eZ80Acclaim!_5.3.4\lib\std\crt.lib", \ 26 | "C:\Tools\ZiLOG\ZDSII_eZ80Acclaim!_5.3.4\lib\std\crtS.lib", \ 27 | "C:\Tools\ZiLOG\ZDSII_eZ80Acclaim!_5.3.4\lib\std\nokernel.lib", \ 28 | "C:\Tools\ZiLOG\ZDSII_eZ80Acclaim!_5.3.4\lib\zilog\zsldevinitdummy.obj" 29 | 30 | -------------------------------------------------------------------------------- /C/Disassembler/eZ80F92_AGON_Flash.ztgt: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Oscillator 5 | 18432000 6 | 7 | 8 | 000000 9 | C0000 10 | FFFF 11 | true 12 | 13 | 14 | 15 | 0 16 | false 17 | 40000 18 | BFFFF 19 | 20 | 1 21 | false 22 | true 23 | 24 | 25 | 26 | 27 | 1 28 | 8 29 | 2 30 | 9 31 | 32 | 33 | 02 34 | 28 35 | C0 36 | C7 37 | 38 | 39 | 81 40 | 28 41 | 80 42 | BF 43 | 44 | 45 | 02 46 | 00 47 | 00 48 | 00 49 | 50 | 51 | 52 | 0 53 | ff 54 | true 55 | true 56 | 57 | 1 58 | 59 | eZ80F92 60 | 1.0.1 61 | 1.00 62 | 63 | -------------------------------------------------------------------------------- /C/Disassembler/init.asm: -------------------------------------------------------------------------------- 1 | ; 2 | ; Title: Disassembler - Initialisation Code 3 | ; Author: Dean Belfield 4 | ; Created: 18/12/2022 5 | ; Last Updated: 18/12/2022 6 | ; 7 | ; Modinfo: 8 | 9 | SEGMENT CODE 10 | 11 | XREF __low_bss 12 | XREF __len_bss 13 | 14 | XREF _main 15 | 16 | XDEF _putch 17 | XDEF _getch 18 | 19 | XDEF __putch 20 | XDEF __getch 21 | 22 | XDEF _errno 23 | 24 | .ASSUME ADL = 1 25 | 26 | argv_ptrs_max: EQU 16 ; Maximum number of arguments allowed in argv 27 | 28 | ; 29 | ; Start in ADL mode 30 | ; 31 | 32 | JP _start ; Jump to start 33 | 34 | ; 35 | ; The header stuff 36 | ; 37 | _exec_name: DB "DISASSEMBLE.BIN", 0 ; The executable name, only used in argv 38 | 39 | ALIGN 64 ; The executable header is from byte 64 onwards 40 | 41 | DB "MOS" ; Flag for MOS - to confirm this is a valid MOS command 42 | DB 00h ; MOS header version 0 43 | DB 01h ; Flag for run mode (0: Z80, 1: ADL) 44 | 45 | ; And the code follows on immediately after the header 46 | ; 47 | _start: PUSH AF ; Preserve registers 48 | PUSH BC 49 | PUSH DE 50 | PUSH IX 51 | PUSH IY ; Need to preserve IY for MOS 52 | ; 53 | PUSH HL ; Clear the RAM 54 | CALL _clear_bss 55 | POP HL 56 | ; 57 | LD IX, argv_ptrs ; The argv array pointer address 58 | PUSH IX ; Parameter 2: argv[0] = IX 59 | CALL _parse_params ; Parse the parameters 60 | LD B, 0 ; Clear B from BCU as we just want ARGC 61 | PUSH BC ; Parameter 1: argc 62 | CALL _main ; int main(int argc, char *argv[]) 63 | POP DE ; Balance the stack 64 | POP DE 65 | ; 66 | POP IY ; Restore registers 67 | POP IX 68 | POP DE 69 | POP BC 70 | POP AF 71 | RET 72 | 73 | ; Clear the memory 74 | ; 75 | _clear_bss: LD BC, __len_bss ; Check for non-zero length 76 | LD a, __len_bss >> 16 77 | OR A, C 78 | OR A, B 79 | RET Z ; BSS is zero-length ... 80 | XOR A, A 81 | LD (__low_bss), A 82 | SBC HL, HL ; HL = 0 83 | DEC BC ; 1st byte's taken care of 84 | SBC HL, BC 85 | RET Z ; Just 1 byte ... 86 | LD HL, __low_bss ; Reset HL 87 | LD DE, __low_bss + 1 ; [DE] = bss + 1 88 | LDIR ; Clear this section 89 | RET 90 | 91 | ; Parse the parameter string into a C array 92 | ; Parameters 93 | ; - HL: Address of parameter string 94 | ; - IX: Address for array pointer storage 95 | ; Returns: 96 | ; - C: Number of parameters parsed 97 | ; 98 | _parse_params: LD BC, _exec_name 99 | LD (IX+0), BC ; ARGV[0] = the executable name 100 | INC IX 101 | INC IX 102 | INC IX 103 | CALL _skip_spaces ; Skip HL past any leading spaces 104 | ; 105 | LD BC, 1 ; C: ARGC = 1 - also clears out top 16 bits of BCU 106 | LD B, argv_ptrs_max - 1 ; B: Maximum number of argv_ptrs 107 | ; 108 | _parse_params_1: 109 | PUSH BC ; Stack ARGC 110 | PUSH HL ; Stack start address of token 111 | CALL _get_token ; Get the next token 112 | LD A, C ; A: Length of the token in characters 113 | POP DE ; Start address of token (was in HL) 114 | POP BC ; ARGC 115 | OR A ; Check for A=0 (no token found) OR at end of string 116 | RET Z 117 | ; 118 | LD (IX+0), DE ; Store the pointer to the token 119 | PUSH HL ; DE=HL 120 | POP DE 121 | CALL _skip_spaces ; And skip HL past any spaces onto the next character 122 | XOR A 123 | LD (DE), A ; Zero-terminate the token 124 | INC IX 125 | INC IX 126 | INC IX ; Advance to next pointer position 127 | INC C ; Increment ARGC 128 | LD A, C ; Check for C >= A 129 | CP B 130 | JR C, _parse_params_1 ; And loop 131 | RET 132 | 133 | ; Get the next token 134 | ; Parameters: 135 | ; - HL: Address of parameter string 136 | ; Returns: 137 | ; - HL: Address of first character after token 138 | ; - C: Length of token (in characters) 139 | ; 140 | _get_token: LD C, 0 ; Initialise length 141 | $$: LD A, (HL) ; Get the character from the parameter string 142 | OR A ; Exit if 0 (end of parameter string in MOS) 143 | RET Z 144 | CP 13 ; Exit if CR (end of parameter string in BBC BASIC) 145 | RET Z 146 | CP ' ' ; Exit if space (end of token) 147 | RET Z 148 | INC HL ; Advance to next character 149 | INC C ; Increment length 150 | JR $B 151 | 152 | ; Skip spaces in the parameter string 153 | ; Parameters: 154 | ; - HL: Address of parameter string 155 | ; Returns: 156 | ; - HL: Address of next none-space character 157 | ; F: Z if at end of string, otherwise NZ if there are more tokens to be parsed 158 | ; 159 | _skip_spaces: LD A, (HL) ; Get the character from the parameter string 160 | CP ' ' ; Exit if not space 161 | RET NZ 162 | INC HL ; Advance to next character 163 | JR _skip_spaces ; Increment length 164 | 165 | ; Write a character out to the ESP32 166 | ; int putch(int ch) 167 | ; 168 | __putch: 169 | _putch: PUSH IY 170 | LD IY, 0 171 | ADD IY, SP 172 | LD A, (IY+6) 173 | RST.LIL 10h 174 | LD HL, 0 175 | LD L, A 176 | LD SP, IY 177 | POP IY 178 | RET 179 | 180 | ; Read a character in from the ESP32 181 | ; int getch(void) 182 | ; 183 | __getch: 184 | _getch: LD HL, 0 185 | RET 186 | 187 | SEGMENT DATA 188 | 189 | 190 | ; Storage for the argv array pointers 191 | ; 192 | argv_ptrs: BLKP argv_ptrs_max, 0 193 | 194 | SEGMENT BSS ; This section is reset to 0 195 | 196 | _errno: DS 3 ; extern int _errno 197 | 198 | END 199 | -------------------------------------------------------------------------------- /C/Disassembler/main.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Title: Disassembler - Main 3 | * Author: Dean Belfield 4 | * Created: 18/12/2022 5 | * Last Updated: 30/03/2023 6 | * 7 | * Based upon information in http://www.z80.info/decoding.htm 8 | * 9 | * Modinfo: 10 | * 04/01/2023: Optimisations 11 | * 16/01/2023: Additional eZ80 instructions; SLP, RSMIX, IN0, OUT0 and the extra block instructions 12 | * 18/01/2023: Additional eZ80 instructions: LD A,MB/LD MB,A, TSTIO, LEA, PEA 13 | * 21/01/2023: Added eZ80 addressing modes, fixed LD, ADD, INC, DEC for IX and IY; fixed column widths, t_alu format 14 | * 27/01/2023: Fixed default ADL mode, LD SP, EX (SP) and JP (rr) for IX and IY 15 | * 30/03/2023: Fixed decode bug in LD [rp],(Mmn) 16 | */ 17 | 18 | #include 19 | #include 20 | #include 21 | 22 | // Storage for the opcode decoder 23 | // 24 | struct s_opcode { 25 | long address; // Start address of the opcode 26 | long count; // Size of the opcode in bytes 27 | unsigned char addressMode; // Addressing mode (0-5) 28 | unsigned char shift; // Shift byte (0X00, 0xCB, 0xDD, 0xED, 0xFD) 29 | unsigned char byteData[8]; // The byte data 30 | char text[32]; // Storage for the opcode text 31 | }; 32 | 33 | void help(void); 34 | void pad(int count, char c); 35 | int parseNumber(char * ptr, long * value); 36 | unsigned char decodeByte(long * address, struct s_opcode * opcode); 37 | long decodeWord(long * address, struct s_opcode * opcode); 38 | long decodeJR(long * address, struct s_opcode * opcode); 39 | void decodeOperand(long * address, struct s_opcode * opcode); 40 | void decodeOperandCB(long * address, struct s_opcode * opcode); 41 | void decodeOperandED(long * address, struct s_opcode * opcode); 42 | 43 | extern int errno; // errno - used by stdlib 44 | extern int putch(int ch); // In init.asm 45 | extern int getch(void); 46 | 47 | long adl; // ADL mode 48 | 49 | // Lookup tables 50 | // 51 | const char * t_r[3][8] = { 52 | { "B", "C", "D", "E", "H", "L", "(HL)", "A" }, 53 | { "B", "C", "D", "E", "IXH", "IXL", "(IX)", "A" }, 54 | { "B", "C", "D", "E", "IYH", "IYL", "(IY)", "A" } 55 | }; 56 | const char * t_rp[8][4] = { 57 | { "BC", "DE", "HL", "SP" }, 58 | { "BC", "DE", "IX", "SP" }, 59 | { "BC", "DE", "IY", "SP" }, 60 | { "BC", "DE", "HL", "AF" }, 61 | { "BC", "DE", "IX", "AF" }, 62 | { "BC", "DE", "IY", "AF" }, 63 | { "BC", "DE", "HL", "IX" }, 64 | { "BC", "DE", "HL", "IY" } 65 | }; 66 | const char * t_shift[] = { "HL", "IX", "IY" }; 67 | const char * t_cc[] = { "NZ", "Z", "NC", "C", "PO", "PE", "P", "M" }; 68 | const char * t_alu[] = { "ADD", "ADC", "SUB", "SBC", "AND", "XOR", "OR", "CP" }; 69 | const char * t_rot[] = { "RLC", "RRC", "RL", "RR", "SLA", "SRA", "SLL", "SRL"}; 70 | const char * t_bit[] = { "BIT", "RES", "SET" }; 71 | const char * t_im[] = { "0", "0/1", "1", "2" }; 72 | const char * t_io[] = { "IN", "OUT" }; 73 | const char * t_inc[] = { "INC", "DEC" }; 74 | const char * t_bl1[4][5] = { 75 | { "LDI", "CPI", "INI", "OUTI", "OUTI2" }, 76 | { "LDD", "CPD", "IND", "OUTD", "OUTD2" }, 77 | { "LDIR", "CPIR", "INIR", "OTIR", "OTI2R" }, 78 | { "LDDR", "CPDR", "INDR", "OTDR", "OTD2R" } 79 | }; 80 | const char * t_bl2[4][3] = { 81 | { "INIM", "OTIM", "INI2" }, 82 | { "INDM", "OTDM", "IND2" }, 83 | { "INIMR", "OTIMR", "INI2R" }, 84 | { "INDMR", "OTDMR", "IND2R" } 85 | }; 86 | const char * t_o1[] = { "RLCA", "RRCA", "RLA", "RRA", "DAA", "CPL", "SCF", "CCF" }; 87 | const char * t_o2[] = { "LD I,A", "LD R,A", "LD A,I", "LD A,R", "RRD", "RLD", "NOP", "NOP" }; 88 | const char * t_am[] = { "", ".SIS", ".LIS", ".SIL", ".LIL" }; 89 | 90 | // Parameters: 91 | // - argc: Argument count 92 | // - argv: Pointer to the argument string - zero terminated, parameters separated by spaces 93 | // 94 | int main(int argc, char * argv[]) { 95 | struct s_opcode opcode; 96 | long address; 97 | long count; 98 | int i; 99 | char c; 100 | 101 | adl = 1; // Default ADL mode 102 | 103 | if(argc < 3 || argc > 4) { 104 | help(); 105 | return 0; 106 | } 107 | 108 | if( !parseNumber(argv[1], &address) || 109 | !parseNumber(argv[2], &count) 110 | ) { 111 | return 19; 112 | } 113 | 114 | if(argc == 4) { 115 | if(!parseNumber(argv[3], &adl)) return 19; 116 | } 117 | 118 | while(count > 0) { 119 | opcode.shift = 0x00; 120 | opcode.addressMode = 0x00; 121 | opcode.text[0] = '\0'; 122 | opcode.address = address; 123 | opcode.count = 0; 124 | decodeOperand(&address, &opcode); 125 | if(opcode.addressMode > 0) { 126 | decodeOperand(&address, &opcode); 127 | } 128 | if(opcode.shift > 0) { 129 | decodeOperand(&address, &opcode); 130 | } 131 | printf("%06X ", opcode.address); 132 | for(i=0; i 31 && c < 127) ? c : '.'); 139 | } 140 | pad((6 - opcode.count) , ' '); 141 | printf(" %s\n\r", opcode.text); 142 | count -= opcode.count; 143 | } 144 | return 0; 145 | } 146 | 147 | void pad(int count, char c) { 148 | int i; 149 | 150 | for(i=0; ibyteData[opcode->count++] = b; 196 | return b; 197 | } 198 | 199 | // Decode a relative jump 200 | // Parameters: 201 | // - address: Pointer to the address counter 202 | // - opcode: Pointer to the opcode structure 203 | // Returns: 204 | // - long: Word 205 | // 206 | long decodeJR(long * address, struct s_opcode * opcode) { 207 | char b; 208 | 209 | b = *(char *)((*address)++); 210 | opcode->byteData[opcode->count++] = b; 211 | return (*address) + b; 212 | } 213 | 214 | // Decode a word (2 or 3 bytes, depending upon ADL mode 215 | // Parameters: 216 | // - address: Pointer to the address counter 217 | // - opcode: Pointer to the opcode structure 218 | // Returns: 219 | // - long: Word 220 | // 221 | long decodeWord(long * address, struct s_opcode * opcode) { 222 | unsigned char l, h, u, am; 223 | 224 | am = opcode->addressMode; 225 | 226 | l = *(long *)((*address)++); 227 | h = *(long *)((*address)++); 228 | 229 | opcode->byteData[opcode->count++] = l; 230 | opcode->byteData[opcode->count++] = h; 231 | 232 | // 2 or 3 byte fetches are determined by ADL mode AND opcode->addressMode 233 | // 234 | if(adl == 1 || am >= 3) { 235 | // 236 | // Word size = 3; fetch a 24-bit word from the code 237 | // 238 | u = *(long *)((*address)++); 239 | opcode->byteData[opcode->count++] = u; 240 | } 241 | else { 242 | // 243 | // Word size = 2; fetch a 16-bit word, and set bits 16-23 to the segment address 244 | // 245 | u = (*address & 0xFF0000) >> 16; 246 | } 247 | return l | (h << 8) | (u << 16); 248 | } 249 | 250 | // Decode an opcode 251 | // Parameters: 252 | // - address: Pointer to the address counter 253 | // - opcode: Pointer to the opcode structure 254 | // 255 | void decodeOperand(long * address, struct s_opcode * opcode) { 256 | unsigned char b; 257 | unsigned char x, y, z, p, q; 258 | char * t; 259 | int shift = 0; 260 | 261 | unsigned char am = opcode->addressMode; 262 | 263 | switch(opcode->shift) { 264 | // 265 | // Handle CB prefixed instructions (rolls, shifts, and bit operations) 266 | // 267 | case 0xCB: { 268 | return decodeOperandCB(address, opcode); 269 | } break; 270 | // 271 | // Handle ED prefixed instructions (miscellanous operations) 272 | // 273 | case 0xED: { 274 | return decodeOperandED(address, opcode); 275 | } break; 276 | // 277 | // IX 278 | // 279 | case 0xDD: { 280 | shift = 1; 281 | } break; 282 | // 283 | // IY 284 | // 285 | case 0xFD: { 286 | shift = 2; 287 | } break; 288 | } 289 | 290 | b = *(long *)((*address)++); // Fetch the byte and increment the pointer 291 | 292 | x = (b & 0xC0) >> 6; // 0b11000000 293 | y = (b & 0x38) >> 3; // 0b00111000 294 | z = (b & 0X07); // 0b00000111 295 | p = y >> 1; // 0b00110000 01 110 111 x=1 y=6 z=7 296 | q = y & 1; // 0b00001000 297 | 298 | // Extra eZ80 instructions added: 299 | // 300 | // LD (IX/Y+n),BC 0x0F = 0b00001111: x=0,y=1,z=7,p=0,q=1 301 | // LD (IX/Y+n),DE 0x1F = 0b00011111: x=0,y=3,z=7,p=1,q=1 302 | // LD (IX/Y+n),HL 0x2F = 0b00101111: x=0,y=5,z=7,p=2,q=1 303 | // LD BC,(IX/Y+n) 0x07 = 0b00000111: x=0,y=0,z=7,p=0,q=0 304 | // LD DE,(IX/Y+n) 0x17 = 0b00010111: x=0,y=2,z=7,p=1,q=0 305 | // LD HL,(IX/Y+n) 0x27 = 0b00100111: x=0,y=4,z=7,p=2,q=0 306 | 307 | // Extra eZ80 suffixes for addressing modes added: 308 | // 309 | // .SIS (LD B,B) 0x40 = 0b01000000: x=1,y=0,z=0 310 | // .LIS (LD C,C) 0x49 = 0b01001001: x=1,y=1,z=1 311 | // .SIL (LD D,D) 0x52 = 0b01010010: x=1,y=2,z=2 312 | // .LIL (LD E,E) 0x5B = 0b01011011: x=1,y=3,z=3 313 | 314 | t = opcode->text; 315 | 316 | opcode->byteData[opcode->count++] = b; 317 | 318 | switch(x) { 319 | // 320 | // X=0 321 | // 322 | case 0: { 323 | switch(z) { 324 | // 325 | // Z=0: Relative jumps and assorted ops 326 | // 327 | case 0: { 328 | switch(y) { 329 | case 0: { 330 | strcpy(t, "NOP"); 331 | } break; 332 | case 1: { 333 | strcpy(t, "EX AF,AF'"); 334 | } break; 335 | case 2: { 336 | sprintf(t, "DJNZ &%06X", decodeJR(address, opcode)); 337 | } break; 338 | case 3: { 339 | sprintf(t, "JR &%06X", decodeJR(address, opcode)); 340 | } break; 341 | default: { 342 | sprintf(t, "JR %s,&%06X", t_cc[y-4], decodeJR(address, opcode)); 343 | } break; 344 | } 345 | } break; 346 | // 347 | // Z=1: 16-bit load immediate/add 348 | // 349 | case 1: { 350 | if(q == 0) { 351 | sprintf(t, "LD%s %s,&%06X", t_am[am], t_rp[shift][p], decodeWord(address, opcode)); 352 | } 353 | else { 354 | sprintf(t, "ADD%s %s,%s", t_am[am], t_rp[shift][2], t_rp[0][p]); 355 | } 356 | } break; 357 | // 358 | // Z=2: Indirect load 359 | // 360 | case 2: { 361 | if(q == 0) { 362 | switch(p) { 363 | case 0: { 364 | strcpy(t, "LD (BC),A"); 365 | } break; 366 | case 1: { 367 | strcpy(t, "LD (DE),A"); 368 | } break; 369 | case 2: { 370 | sprintf(t, "LD%s (&%06X),HL", t_am[am], decodeWord(address, opcode)); 371 | } break; 372 | case 3: { 373 | sprintf(t, "LD%s (&%06X),A", t_am[am], decodeWord(address, opcode)); 374 | } break; 375 | } 376 | } 377 | else { 378 | switch(p) { 379 | case 0: { 380 | strcpy(t, "LD A,(BC)"); 381 | } break; 382 | case 1: { 383 | strcpy(t, "LD A,(DE)"); 384 | } break; 385 | case 2: { 386 | sprintf(t, "LD%s HL,(&%06X)", t_am[am], decodeWord(address, opcode)); 387 | } break; 388 | case 3: { 389 | sprintf(t, "LD%s A,(&%06X)", t_am[am], decodeWord(address, opcode)); 390 | } break; 391 | } 392 | 393 | } 394 | } break; 395 | // 396 | // Z=3: 16-bit increment/decrement 397 | // 398 | case 3: { 399 | sprintf(t, "%s%s %s", t_inc[q], t_am[am], t_rp[3+shift][p]); 400 | } break; 401 | // 402 | // Z=4: 8-bit increment 403 | // Z=5: 8-bit decrement 404 | // 405 | case 4: 406 | case 5: { 407 | sprintf(t, "%s %s", t_inc[z-4], t_r[shift][y]); 408 | } break; 409 | // 410 | // Z=6: 8-bit load immediate 411 | // 412 | case 6: { 413 | sprintf(t, "LD %s,&%02X", t_r[shift][y], decodeByte(address, opcode)); 414 | } break; 415 | // 416 | // Z=7: Assorted operations on accumulator flags / LD (IX/Y+n),rr / LD rr, (IX/Y+n) 417 | // 418 | case 7: { 419 | if(shift == 0) { 420 | strcpy(t, t_o1[y]); 421 | } 422 | else { 423 | if(q == 0) { 424 | sprintf(t, "LD %s,(%s%+d)", t_rp[shift][p], t_shift[shift], (char)decodeByte(address, opcode)); 425 | } 426 | else { 427 | sprintf(t, "LD (%s%+d),%s", t_shift[shift], (char)decodeByte(address, opcode), t_rp[shift][p]); 428 | } 429 | } 430 | } break; 431 | } 432 | } break; 433 | // 434 | // X = 1 435 | // 436 | case 1: { 437 | // 438 | // Select an addressing mode 439 | // 440 | if(y == z && y < 4) { 441 | opcode->addressMode = y + 1; 442 | } 443 | else if(y == 6 && z == 6) { 444 | strcpy(t, "HALT"); 445 | } 446 | else { 447 | // 448 | // LD (IX/Y+d),r 449 | // LD r,(IX/Y+d) 450 | // 451 | if(shift > 0 && (y == 6 || z == 6)) { 452 | if(y == 6) { 453 | sprintf(t, "LD%s (%s%+d),%s", t_am[am], t_shift[shift], (char)decodeByte(address, opcode), t_r[0][z]); 454 | } 455 | else if(z == 6) { 456 | sprintf(t, "LD%s %s,(%s%+d)", t_am[am], t_r[0][y], t_shift[shift], (char)decodeByte(address, opcode)); 457 | } 458 | } 459 | // 460 | // LD r,r' 461 | // 462 | else { 463 | sprintf(t, "LD%s %s,%s", t_am[am], t_r[shift][y], t_r[shift][z]); 464 | } 465 | } 466 | } break; 467 | // 468 | // X = 2: ALU operations 469 | // 470 | case 2: { 471 | sprintf(t, "%s A,%s", t_alu[y], t_r[shift][z]); 472 | } break; 473 | // 474 | // X = 3 475 | // 476 | case 3: { 477 | switch(z) { 478 | // 479 | // Z=0: Conditional return 480 | // 481 | case 0: { 482 | sprintf(t, "RET%s %s", t_am[am], t_cc[y]); 483 | } break; 484 | // 485 | // Z=1: POP and various operations 486 | // 487 | case 1: { 488 | if(q == 0) { 489 | sprintf(t, "POP%s %s", t_am[am], t_rp[3+shift][p]); 490 | } 491 | else { 492 | switch(p) { 493 | case 0: { 494 | sprintf(t, "RET%s", t_am[am]); 495 | } break; 496 | case 1: { 497 | strcpy(t, "EXX"); 498 | } break; 499 | case 2: { 500 | sprintf(t, "JP%s (%s)", t_am[am], t_rp[shift][2]); 501 | } break; 502 | case 3: { 503 | sprintf(t, "LD%s SP,%s", t_am[am], t_rp[shift][2]); 504 | } break; 505 | } 506 | } 507 | 508 | } break; 509 | // 510 | // Z=2: Conditional jump 511 | // 512 | case 2: { 513 | sprintf(t, "JP%s %s,&%06X", t_am[am], t_cc[y], decodeWord(address, opcode)); 514 | } break; 515 | // 516 | // Z=3: Assorted operations 517 | // 518 | case 3: { 519 | switch(y) { 520 | case 0: { 521 | sprintf(t, "JP%s &%06X", t_am[am], decodeWord(address, opcode)); 522 | } break; 523 | case 1: { 524 | opcode->shift = 0xCB; 525 | } break; 526 | case 2: { 527 | sprintf(t, "OUT (&%02X),A", decodeByte(address, opcode)); 528 | } break; 529 | case 3: { 530 | sprintf(t, "IN (&%02X),A", decodeByte(address, opcode)); 531 | } break; 532 | case 4: { 533 | sprintf(t, "EX (SP),%s", t_rp[shift][2]); 534 | } break; 535 | case 5: { 536 | strcpy(t, "EX DE,HL"); 537 | } break; 538 | case 6: { 539 | strcpy(t, "DI"); 540 | } break; 541 | case 7: { 542 | strcpy(t, "EI"); 543 | } break; 544 | } 545 | } break; 546 | // 547 | // Z=4: Conditional call 548 | // 549 | case 4: { 550 | sprintf(t, "CALL%s %s,&%06X", t_am[am], t_cc[y], decodeWord(address, opcode)); 551 | } break; 552 | // 553 | // Z=5: PUSH and various operations 554 | // 555 | case 5: { 556 | if(q == 0) { 557 | sprintf(t, "PUSH%s %s", t_am[am], t_rp[3+shift][p]); 558 | } 559 | else { 560 | switch(p) { 561 | case 0: { 562 | sprintf(t, "CALL%s &%06X", t_am[am], decodeWord(address, opcode)); 563 | } break; 564 | case 1: { 565 | opcode->shift = 0xDD; 566 | } break; 567 | case 2: { 568 | opcode->shift = 0xED; 569 | } break; 570 | case 3: { 571 | opcode->shift = 0xFD; 572 | } break; 573 | } 574 | } 575 | } break; 576 | // 577 | // Z=6: Operate on accumulator and immediate operand 578 | // 579 | case 6: { 580 | sprintf(t, "%s A,&%02X", t_alu[y], decodeByte(address, opcode)); 581 | } break; 582 | // 583 | // Z=7: Restart instructions 584 | // 585 | case 7: { 586 | sprintf(t, "RST%s &%02X", t_am[am], y<<3); 587 | } break; 588 | } break; 589 | } break; 590 | } 591 | } 592 | 593 | // Decode an opcode (CB-prefixed operands) 594 | // Parameters: 595 | // - address: Pointer to the address counter 596 | // - opcode: Pointer to the opcode structure 597 | // 598 | void decodeOperandCB(long * address, struct s_opcode * opcode) { 599 | unsigned char b; 600 | unsigned char x, y, z, p, q; 601 | char * t; 602 | 603 | b = *(long *)((*address)++); // Fetch the byte and increment the pointer 604 | 605 | x = (b & 0xC0) >> 6; // 0b11000000 606 | y = (b & 0x38) >> 3; // 0b00111000 607 | z = (b & 0X07); // 0b00000111 608 | p = y >> 1; // 0b00110000 609 | q = y & 1; // 0b00001000 610 | 611 | t = opcode->text; 612 | 613 | opcode->byteData[opcode->count++] = b; 614 | 615 | switch(x) { 616 | // 617 | // X=0: Rotate and Shift Operands 618 | // 619 | case 0: { 620 | sprintf(t, "%s %s", t_rot[y], t_r[0][z]); 621 | } break; 622 | // 623 | // X=1: BIT 624 | // X=2: RES 625 | // X=3: SET 626 | // 627 | case 1: 628 | case 2: 629 | case 3: { 630 | sprintf(t, "%s %d,%s", t_bit[x-1], y, t_r[0][z]); 631 | } break; 632 | } 633 | } 634 | 635 | // Decode an opcode (ED-prefixed operands) 636 | // Parameters: 637 | // - address: Pointer to the address counter 638 | // - opcode: Pointer to the opcode structure 639 | // 640 | void decodeOperandED(long * address, struct s_opcode * opcode) { 641 | unsigned char b; 642 | unsigned char x, y, z, p, q; 643 | char * t; 644 | 645 | unsigned char am = opcode->addressMode; 646 | 647 | b = *(long *)((*address)++); // Fetch the byte and increment the pointer 648 | 649 | x = (b & 0xC0) >> 6; // 0b11000000 650 | y = (b & 0x38) >> 3; // 0b00111000 651 | z = (b & 0X07); // 0b00000111 652 | p = y >> 1; // 0b00110000 653 | q = y & 1; // 0b00001000 654 | 655 | // Extra eZ80 instructions added: 656 | // 657 | // IN0 B,(N) 0x00 = 0b00000000: x=0,z=0,y=0 658 | // IN0 C,(N) 0x08 = 0b00001000: x=0,z=0,y=1 659 | // IN0 D,(N) 0x10 = 0b00010000: x=0,z=0,y=2 660 | // IN0 E,(N) 0x18 = 0b00011000: x=0,z=0,y=3 661 | // IN0 H,(N) 0x20 = 0b00100000: x=0,z=0,y=4 662 | // IN0 L,(N) 0x28 = 0b00101000: x=0,z=0,y=5 663 | // IN0 A,(N) 0x38 = 0b00111000: x=0,z=0,y=7 664 | // 665 | // OUT0 (N),B 0x01 = 0b00000001: x=0,z=1,y=0 666 | // OUT0 (N),C 0x09 = 0b00001001: x=0,z=1,y=1 667 | // OUT0 (N),D 0x11 = 0b00010001: x=0,z=1,y=2 668 | // OUT0 (N),E 0x19 = 0b00011001: x=0,z=1,y=3 669 | // OUT0 (N),H 0x21 = 0b00100001: x=0,z=1,y=4 670 | // OUT0 (N),L 0x29 = 0b00101001: x=0,z=1,y=5 671 | // OUT0 (N),A 0x39 = 0b00111001: x=0,z=1,y=7 672 | // 673 | // LEA BC, IX+d 0x02 = 0b00000010: x=0,z=2,y=0 674 | // LEA DE, IX+d 0x12 = 0b00010010: x=0,z=2,y=2 675 | // LEA HL, IX+d 0x22 = 0b00100010: x=0,z=2,y=4 676 | // LEA IX, IX+d 0X32 = 0b00110010: x=0,z=2,y=6 677 | 678 | // LEA BC, IY+d 0x03 = 0b00000011: x=0,z=3,y=0 679 | // LEA DE, IY+d 0x13 = 0b00010011: x=0,z=3,y=2 680 | // LEA HL, IY+d 0x23 = 0b00100011: x=0,z=3,y=4 681 | // LEA IY, IY+d 0X33 = 0b00110011: x=0,z=3,y=6 682 | // 683 | // TST A,B 0x04 = 0b00000100: x=0,z=4,y=0 684 | // TST A,C 0X0C = 0b00001100: x=0,z=4,y=1 685 | // TST A,D 0x14 = 0b00010100: x=0,z=4,y=2 686 | // TST A,E 0x1C = 0b00011100: x=0,z=4,y=3 687 | // TST A,H 0x24 = 0b00100100: x=0,z=4,y=4 688 | // TST A,L 0x2C = 0b00101100: x=0,z=4,y=5 689 | // TST A,(HL) 0x34 = 0b00110100: x=0,z=4,y=6 690 | // TST A,A 0x3C = 0b00111100: x=0,z=4,y=7 691 | // 692 | // LD BC,(HL) 0x07 = 0b00000111: x=0,z=7,y=0 693 | // LD (HL),BC 0x0F = 0b00001111: x=0,z=7,y=1 694 | // LD DE,(HL) 0x17 = 0b00010111: x=0,z=7,y=2 695 | // LD (HL),DE 0x1F = 0b00011111: x=0,z=7,y=3 696 | // LD HL,(HL) 0x27 = 0b00100111: x=0,z=7,y=4 697 | // LD (HL),HL 0x2F = 0b00101111: x=0,z=7,y=5 698 | // 699 | // MLT BC 0x4C = 0b01001100: x=1,z=4,y=1 700 | // LEA IX, IY+d 0X54 = 0b01010100: x=1,z=4,y=2 701 | // MLT DE 0x5C = 0b01011100: x=1,z=4,y=3 702 | // TST A,n 0x64 = 0b01100100: x=1,z=4,y=4 703 | // MTL HL 0x6C = 0b01101100: x=1,z=4,y=5 704 | // TSTIO n 0x74 = 0b01110100: x=1,z=4,y=6 705 | 706 | // LEA IY, IX+d 0X55 = 0b01010101: x=1,z=5,y=2 707 | // PEA IX+d 0x65 = 0b01100101: x=1,z=5,y=4 708 | // LD MB, A 0x6D = 0b01101101: x=1,z=5,y=5 709 | // STMIX 0x7D = 0b01111101: x=1,z=5,y=7 710 | 711 | // PEA IY-d 0x66 = 0b01100110: x=y,z=6,y=4 712 | // LD A, MB 0x6E = 0b01101110: x=1,z=6,y=5 713 | // SLP 0x76 = 0b01110110: x=1,z=6,y=6 714 | // RSMIX 0x7E = 0b01111110: x=1,z=6,y=7 715 | 716 | // Plus the block instructions 717 | 718 | t = opcode->text; 719 | 720 | opcode->byteData[opcode->count++] = b; 721 | 722 | switch(x) { 723 | // 724 | // X = 0 725 | // 726 | case 0: { 727 | switch(z) { 728 | case 0: { 729 | sprintf(t, "IN0 %s,(&%02X)", t_r[0][y], decodeByte(address, opcode)); 730 | } break; 731 | case 1: { 732 | sprintf(t, "OUT0 (&%02X),%s", decodeByte(address, opcode), t_r[0][y]); 733 | } break; 734 | case 2: { 735 | sprintf(t, "LEA %s,IX%+d", t_rp[6][y>>1], (char)decodeByte(address, opcode)); 736 | } break; 737 | case 3: { 738 | sprintf(t, "LEA %s,IX%+d", t_rp[7][y>>1], (char)decodeByte(address, opcode)); 739 | } break; 740 | case 4: { 741 | sprintf(t, "TST A,%s", t_r[0][y]); 742 | } break; 743 | case 7: { 744 | if(q == 0) { 745 | sprintf(t, "LD %s,(HL)", t_rp[0][p]); 746 | } 747 | else { 748 | sprintf(t, "LD (HL),%s", t_rp[0][p]); 749 | } 750 | } break; 751 | } 752 | } break; 753 | // 754 | // X = 1 755 | // 756 | case 1: { 757 | switch(z) { 758 | case 0: 759 | case 1: { 760 | if(y != 6) { 761 | sprintf(t, "%s %s,(C)", t_io[z], t_r[0][y]); 762 | } 763 | else { 764 | sprintf(t, "%s (C)", t_io[z]); 765 | } 766 | } break; 767 | case 2: { 768 | if(q == 0) { 769 | sprintf(t, "SBC HL,%s", t_rp[0][p]); 770 | } 771 | else { 772 | sprintf(t, "ADC HL,%s", t_rp[0][p]); 773 | } 774 | } break; 775 | case 3: { 776 | if(q == 0) { 777 | sprintf(t, "LD (&%06X),%s", decodeWord(address, opcode), t_rp[0][p]); 778 | } 779 | else { 780 | sprintf(t, "LD %s,(&%06X)", t_rp[0][p], decodeWord(address, opcode)); 781 | } 782 | } break; 783 | case 4: { 784 | switch(y) { 785 | case 0: { 786 | strcpy(t, "NEG"); 787 | } break; 788 | case 1: { 789 | strcpy(t, "MLT BC"); 790 | } break; 791 | case 2: { 792 | sprintf(t, "LEA IX,IY%+d", (char)decodeByte(address, opcode)); 793 | } break; 794 | case 3: { 795 | strcpy(t, "MLT DE"); 796 | } break; 797 | case 4: { 798 | sprintf(t, "TST A,&%02X", decodeByte(address, opcode)); 799 | } break; 800 | case 5: { 801 | strcpy(t, "MLT HL"); 802 | } break; 803 | case 6: { 804 | sprintf(t, "TSTIO &%02X", decodeByte(address, opcode)); 805 | } break; 806 | } 807 | } break; 808 | case 5: { 809 | switch(y) { 810 | case 1: { 811 | sprintf(t, "RETI%s", t_am[am]); 812 | } break; 813 | case 2: { 814 | sprintf(t, "LEA IY,IX%+d", (char)decodeByte(address, opcode)); 815 | } break; 816 | case 4: { 817 | sprintf(t, "PEA IX%+d", (char)decodeByte(address, opcode)); 818 | } break; 819 | case 5: { 820 | strcpy(t, "LD MB, A"); 821 | } break; 822 | case 7: { 823 | strcpy(t, "STMIX"); 824 | } break; 825 | default: { 826 | sprintf(t, "RETN%s", t_am[am]); 827 | } break; 828 | } 829 | } break; 830 | case 6: { 831 | switch(y) { 832 | case 4: { 833 | sprintf(t, "PEA IY%+d", (char)decodeByte(address, opcode)); 834 | } break; 835 | case 5: { 836 | strcpy(t, "LD A, MB"); 837 | } break; 838 | case 6: { 839 | strcpy(t, "SLP"); 840 | } break; 841 | case 7: { 842 | strcpy(t, "RSMIX"); 843 | } break; 844 | default: { 845 | sprintf(t, "IM %s", t_im[y]); 846 | } break; 847 | } 848 | } break; 849 | case 7: { 850 | strcpy(t, t_o2[y]); 851 | } break; 852 | } 853 | } break; 854 | // 855 | // X = 2: Block operations 856 | // 857 | case 2: { 858 | if(y < 4) { 859 | if(z >= 2 && z <= 4) { 860 | sprintf(t, "%s%s", t_am[am], t_bl2[y][z-2]); 861 | } 862 | } 863 | else { 864 | if(z <= 4) { 865 | sprintf(t, "%s%s", t_am[am], t_bl1[y-4][z]); 866 | } 867 | } 868 | 869 | } break; 870 | } 871 | } 872 | -------------------------------------------------------------------------------- /C/Hello World/Debug.linkcmd: -------------------------------------------------------------------------------- 1 | -FORMAT=OMF695,INTEL32 2 | -map -maxhexlen=64 -quiet -warnoverlap -xref -unresolved=fatal 3 | -sort NAME=ascending -warn -debug -NOigcase 4 | 5 | ; SEARCHPATH="C:\Tools\ZiLOG\ZDSII_eZ80Acclaim!_5.3.4\lib" 6 | 7 | RANGE ROM $000000 : $01FFFF 8 | RANGE RAM $040000 : $0BFFFF 9 | RANGE EXTIO $000000 : $00FFFF 10 | RANGE INTIO $000000 : $0000FF 11 | 12 | CHANGE CODE = RAM 13 | CHANGE STRSECT = RAM 14 | CHANGE DATA = RAM 15 | 16 | ORDER CODE,DATA 17 | 18 | DEFINE __low_bss = base of BSS 19 | DEFINE __len_bss = length of BSS 20 | 21 | "Hello World"= \ 22 | ".\init.obj", \ 23 | ".\main.obj", \ 24 | "C:\Tools\ZiLOG\ZDSII_eZ80Acclaim!_5.3.4\lib\std\chelpD.lib", \ 25 | "C:\Tools\ZiLOG\ZDSII_eZ80Acclaim!_5.3.4\lib\std\crtD.lib", \ 26 | "C:\Tools\ZiLOG\ZDSII_eZ80Acclaim!_5.3.4\lib\std\crtSD.lib", \ 27 | "C:\Tools\ZiLOG\ZDSII_eZ80Acclaim!_5.3.4\lib\std\nokernelD.lib", \ 28 | "C:\Tools\ZiLOG\ZDSII_eZ80Acclaim!_5.3.4\lib\zilog\zsldevinitdummy.obj" 29 | 30 | -------------------------------------------------------------------------------- /C/Hello World/Hello World.wsp: -------------------------------------------------------------------------------- 1 | [WorkState_v1_2] 2 | ptn_Child1=Frames 3 | 4 | [WorkState_v1_2.Frames] 5 | ptn_Child1=ChildFrames 6 | 7 | [WorkState_v1_2.Frames.ChildFrames] 8 | 9 | -------------------------------------------------------------------------------- /C/Hello World/Hello World.zdsproj: -------------------------------------------------------------------------------- 1 | 2 | eZ80F92 3 | 4 | 5 | 6 | .\init.asm 7 | .\main.c 8 | .\Debug.linkcmd 9 | .\Release.linkcmd 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 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 | 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 | 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 | 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 | 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 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | -------------------------------------------------------------------------------- /C/Hello World/README.md: -------------------------------------------------------------------------------- 1 | # Hello World 2 | 3 | Usage: `hello_c` 4 | 5 | A simple example of a 24-bit ADL application in C 6 | 7 | NB: 8 | - The paths in the link files (Debug.linkcmd and Release.linkcmd) need to be modified to reflect where the tools are located on your hard drive before this will compile. -------------------------------------------------------------------------------- /C/Hello World/Release.linkcmd: -------------------------------------------------------------------------------- 1 | -FORMAT=OMF695,INTEL32 2 | -map -maxhexlen=64 -quiet -warnoverlap -xref -unresolved=fatal 3 | -sort NAME=ascending -warn -debug -NOigcase 4 | 5 | ; SEARCHPATH="C:\Tools\ZiLOG\ZDSII_eZ80Acclaim!_5.3.4\lib" 6 | 7 | RANGE ROM $000000 : $01FFFF 8 | RANGE RAM $0B0000 : $0BFFFF 9 | RANGE EXTIO $000000 : $00FFFF 10 | RANGE INTIO $000000 : $0000FF 11 | 12 | CHANGE CODE = RAM 13 | CHANGE STRSECT = RAM 14 | CHANGE DATA = RAM 15 | 16 | ORDER CODE,DATA 17 | 18 | DEFINE __low_bss = base of BSS 19 | DEFINE __len_bss = length of BSS 20 | 21 | "Hello World"= \ 22 | ".\init.obj", \ 23 | ".\main.obj", \ 24 | "C:\Tools\ZiLOG\ZDSII_eZ80Acclaim!_5.3.4\lib\std\chelp.lib", \ 25 | "C:\Tools\ZiLOG\ZDSII_eZ80Acclaim!_5.3.4\lib\std\crt.lib", \ 26 | "C:\Tools\ZiLOG\ZDSII_eZ80Acclaim!_5.3.4\lib\std\crtS.lib", \ 27 | "C:\Tools\ZiLOG\ZDSII_eZ80Acclaim!_5.3.4\lib\std\nokernel.lib", \ 28 | "C:\Tools\ZiLOG\ZDSII_eZ80Acclaim!_5.3.4\lib\zilog\zsldevinitdummy.obj" 29 | 30 | -------------------------------------------------------------------------------- /C/Hello World/eZ80F92_AGON_Flash.ztgt: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Oscillator 5 | 18432000 6 | 7 | 8 | 000000 9 | C0000 10 | FFFF 11 | true 12 | 13 | 14 | 15 | 0 16 | false 17 | 40000 18 | BFFFF 19 | 20 | 1 21 | false 22 | true 23 | 24 | 25 | 26 | 27 | 1 28 | 8 29 | 2 30 | 9 31 | 32 | 33 | 02 34 | 28 35 | C0 36 | C7 37 | 38 | 39 | 81 40 | 28 41 | 80 42 | BF 43 | 44 | 45 | 02 46 | 00 47 | 00 48 | 00 49 | 50 | 51 | 52 | 0 53 | ff 54 | true 55 | true 56 | 57 | 1 58 | 59 | eZ80F92 60 | 1.0.1 61 | 1.00 62 | 63 | -------------------------------------------------------------------------------- /C/Hello World/init.asm: -------------------------------------------------------------------------------- 1 | ; 2 | ; Title: Hello World - Initialisation Code 3 | ; Author: Dean Belfield 4 | ; Created: 22/11/2022 5 | ; Last Updated: 25/11/2022 6 | ; 7 | ; Modinfo: 8 | ; 25/11/2022: Added parameter parsing; now accepts CR or NUL as end of string markers 9 | 10 | SEGMENT CODE 11 | 12 | XREF __low_bss 13 | XREF __len_bss 14 | 15 | XREF _main 16 | 17 | XDEF _putch 18 | XDEF _getch 19 | 20 | XDEF __putch 21 | XDEF __getch 22 | 23 | .ASSUME ADL = 1 24 | 25 | argv_ptrs_max: EQU 16 ; Maximum number of arguments allowed in argv 26 | 27 | ; 28 | ; Start in ADL mode 29 | ; 30 | 31 | JP _start ; Jump to start 32 | 33 | ; 34 | ; The header stuff 35 | ; 36 | _exec_name: DB "HELLO.BIN", 0 ; The executable name, only used in argv 37 | 38 | ALIGN 64 ; The executable header is from byte 64 onwards 39 | 40 | DB "MOS" ; Flag for MOS - to confirm this is a valid MOS command 41 | DB 00h ; MOS header version 0 42 | DB 01h ; Flag for run mode (0: Z80, 1: ADL) 43 | 44 | ; 45 | ; And the code follows on immediately after the header 46 | ; 47 | _start: PUSH AF ; Preserve registers 48 | PUSH BC 49 | PUSH DE 50 | PUSH IX 51 | PUSH IY ; Need to preserve IY for MOS 52 | ; 53 | PUSH HL ; Clear the RAM 54 | CALL _clear_bss 55 | POP HL 56 | ; 57 | LD IX, argv_ptrs ; The argv array pointer address 58 | PUSH IX ; Parameter 2: argv[0] = IX 59 | CALL _parse_params ; Parse the parameters 60 | LD B, 0 ; Clear B from BCU as we just want ARGC 61 | PUSH BC ; Parameter 1: argc 62 | CALL _main ; int main(int argc, char *argv[]) 63 | POP DE ; Balance the stack 64 | POP DE 65 | 66 | POP IY ; Restore registers 67 | POP IX 68 | POP DE 69 | POP BC 70 | POP AF 71 | RET 72 | 73 | ; Clear the memory 74 | ; 75 | _clear_bss: LD BC, __len_bss ; Check for non-zero length 76 | LD a, __len_bss >> 16 77 | OR A, C 78 | OR A, B 79 | RET Z ; BSS is zero-length ... 80 | XOR A, A 81 | LD (__low_bss), A 82 | SBC HL, HL ; HL = 0 83 | DEC BC ; 1st byte's taken care of 84 | SBC HL, BC 85 | RET Z ; Just 1 byte ... 86 | LD HL, __low_bss ; Reset HL 87 | LD DE, __low_bss + 1 ; [DE] = bss + 1 88 | LDIR ; Clear this section 89 | RET 90 | 91 | ; Parse the parameter string into a C array 92 | ; Parameters 93 | ; - HL: Address of parameter string 94 | ; - IX: Address for array pointer storage 95 | ; Returns: 96 | ; - C: Number of parameters parsed 97 | ; 98 | _parse_params: LD BC, _exec_name 99 | LD (IX+0), BC ; ARGV[0] = the executable name 100 | INC IX 101 | INC IX 102 | INC IX 103 | CALL _skip_spaces ; Skip HL past any leading spaces 104 | ; 105 | LD BC, 1 ; C: ARGC = 1 - also clears out top 16 bits of BCU 106 | LD B, argv_ptrs_max - 1 ; B: Maximum number of argv_ptrs 107 | ; 108 | _parse_params_1: 109 | PUSH BC ; Stack ARGC 110 | PUSH HL ; Stack start address of token 111 | CALL _get_token ; Get the next token 112 | LD A, C ; A: Length of the token in characters 113 | POP DE ; Start address of token (was in HL) 114 | POP BC ; ARGC 115 | OR A ; Check for A=0 (no token found) OR at end of string 116 | RET Z 117 | ; 118 | LD (IX+0), DE ; Store the pointer to the token 119 | PUSH HL ; DE=HL 120 | POP DE 121 | CALL _skip_spaces ; And skip HL past any spaces onto the next character 122 | XOR A 123 | LD (DE), A ; Zero-terminate the token 124 | INC IX 125 | INC IX 126 | INC IX ; Advance to next pointer position 127 | INC C ; Increment ARGC 128 | LD A, C ; Check for C >= A 129 | CP B 130 | JR C, _parse_params_1 ; And loop 131 | RET 132 | 133 | ; Get the next token 134 | ; Parameters: 135 | ; - HL: Address of parameter string 136 | ; Returns: 137 | ; - HL: Address of first character after token 138 | ; - C: Length of token (in characters) 139 | ; 140 | _get_token: LD C, 0 ; Initialise length 141 | $$: LD A, (HL) ; Get the character from the parameter string 142 | OR A ; Exit if 0 (end of parameter string in MOS) 143 | RET Z 144 | CP 13 ; Exit if CR (end of parameter string in BBC BASIC) 145 | RET Z 146 | CP ' ' ; Exit if space (end of token) 147 | RET Z 148 | INC HL ; Advance to next character 149 | INC C ; Increment length 150 | JR $B 151 | 152 | ; Skip spaces in the parameter string 153 | ; Parameters: 154 | ; - HL: Address of parameter string 155 | ; Returns: 156 | ; - HL: Address of next none-space character 157 | ; F: Z if at end of string, otherwise NZ if there are more tokens to be parsed 158 | ; 159 | _skip_spaces: LD A, (HL) ; Get the character from the parameter string 160 | CP ' ' ; Exit if not space 161 | RET NZ 162 | INC HL ; Advance to next character 163 | JR _skip_spaces ; Increment length 164 | 165 | ; Write a character out to the ESP32 166 | ; int putch(int ch) 167 | ; 168 | __putch: 169 | _putch: PUSH IY 170 | LD IY, 0 171 | ADD IY, SP 172 | LD A, (IY+6) 173 | RST.LIL 10h 174 | LD HL, 0 175 | LD L, A 176 | LD SP, IY 177 | POP IY 178 | RET 179 | 180 | ; Read a character in from the ESP32 181 | ; int getch(void) 182 | ; 183 | __getch: 184 | _getch: LD HL, 0 185 | RET 186 | 187 | SEGMENT DATA 188 | 189 | 190 | ; Storage for the argv array pointers 191 | ; 192 | argv_ptrs: BLKP argv_ptrs_max, 0 193 | 194 | END 195 | -------------------------------------------------------------------------------- /C/Hello World/main.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Title: Hello World - C example 3 | * Author: Dean Belfield 4 | * Created: 22/06/2022 5 | * Last Updated: 22/11/2022 6 | * 7 | * Modinfo: 8 | */ 9 | 10 | #include 11 | 12 | // Parameters: 13 | // - argc: Argument count 14 | // - argv: Pointer to the argument string - zero terminated, parameters separated by spaces 15 | // 16 | int main(int argc, char * argv[]) { 17 | int i; 18 | 19 | printf("Hello World\n\r"); 20 | printf("Arguments:\n\r"); 21 | printf("- argc: %d\n\r", argc); 22 | 23 | for(i = 0; i < argc; i++) { 24 | printf("- argv[%d]: %s\n\r", i, argv[i]); 25 | } 26 | return 0; 27 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Dean Belfield 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # agon-projects 2 | 3 | Part of the official Quark firmware for the Agon series of microcomputers 4 | 5 | ### What is the Agon 6 | 7 | Agon is a modern, fully open-source, 8-bit microcomputer and microcontroller in one small, low-cost board. As a computer, it is a standalone device that requires no host PC: it puts out its own video (VGA), audio (2 identical mono channels), accepts a PS/2 keyboard and has its own mass-storage in the form of a µSD card. 8 | 9 | https://www.thebyteattic.com/p/agon.html 10 | 11 | ### The Projects 12 | 13 | Each project is a self-contained executable and can be compiled and tested using the Zilog ZDS tools and USB smart cable. 14 | 15 | The resultant Intel Hex file can be converted to an AGON executable bin file by using a hex2bin utility. If the command is to be executed as a star command from the command line, then the bin file must be copied to a mos directory in the root directory of the SD card. 16 | 17 | For your convenience, I've included precompiled copies of completed projects in the BIN folder of this project. 18 | 19 | - `ASM`: 20 | - `Hello World 16`: A simple example of a Z80 executable (16-bit Z80) 21 | - `Hello World 24`: A simple example of an ADL executable (24-bit Z80) 22 | - `Memory Dump`: A memory hex-dump to screen utility 23 | - `C`: 24 | - `Hello World`: A simple example of a C executable (24-bit 280) 25 | - `Disassembler`: A fully featured eZ80 disassembler 26 | 27 | The C code passes the following values to main 28 | 29 | - `argc`: Number of arguments 30 | - `argv`: An array of pointers to the arguments, the first argument being the executable's name 31 | 32 | The ASM code passes the arguments in a slightly different way: 33 | 34 | - `IX`: Pointer to array of pointers to the parameter strings 35 | - `C`: Number of arguments 36 | 37 | ### The MOS executable format 38 | 39 | The MOS header is stored from bytes 64 in the executable and consists of the following: 40 | 41 | - A three byte ASCII representation of the word MOS 42 | - A single byte for the header version 43 | - A single byte for the executable type: 0 = Z80, 1 = ADL 44 | 45 | It is stored at offset 64 in order for there to be sufficient space for the Z80 in Z80 mode. You can see examples of these in each projects init.asm file. 46 | 47 | ### Prerequisites 48 | 49 | Either add the mos/src folder to the include folder in the project settings or copy the latest [mos_api.inc](https://github.com/breakintoprogram/agon-mos/blob/main/src/mos_api.inc) from the MOS project into the project folder before build. 50 | 51 | ### Testing 52 | 53 | - Each project is designed to assemble/compile in debug mode at address &040000. 54 | - Once loaded, it can be executed using the MOS command `run`. 55 | - Additional parameters can be specified. for example: `run &40000 &100`. Note that you must specify the address of the executable in memory in this instance. 56 | 57 | ### Creating MOS Binary Executable Files 58 | 59 | Z80 executables can be generated directly from the hex file created in the `debug` folder of the project after assembly. 60 | 61 | ADL executables first need to built using the `release` profile. These are not relocatable; the release build will assemble to the final location &0B0000. Create the bin file from the hex file in the `release` folder of the project. 62 | 63 | NB: The ZDS tools currently clear the top of ram during download, which is why we debug them at a lower memory location. 64 | 65 | ### Etiquette 66 | 67 | Please do not issue pull requests or issues for this project; it is very much a work-in-progress. 68 | I will review this policy once the code is approaching live status and I have time to collaborate more. 69 | 70 | ### Build 71 | 72 | This project is designed to be assembled and linked using the Zilog ZDS II toolkit - see the [readme](https://github.com/breakintoprogram/agon-mos/blob/main/README.md#build) in MOS for more details. 73 | 74 | Any custom settings for Agon development is contained within the project files, so no further configuration will need to be done. 75 | 76 | ### Licenses 77 | 78 | All project files are released under an MIT license, unless there is an accompanying license in the project folder 79 | 80 | ### Requirements 81 | 82 | - [ZDS II tools](https://zilog.com/index.php?option=com_zcm&task=view&soft_id=38&Itemid=74) 83 | - A hex to binary convertor, for example [hex2bin](https://hex2bin.sourceforge.net) 84 | 85 | ### Links 86 | 87 | - [Zilog eZ80 User Manual](http://www.zilog.com/docs/um0077.pdf) 88 | - [ZiLOG Developer Studio II User Manual](http://www.zilog.com/docs/devtools/um0144.pdf) --------------------------------------------------------------------------------