├── .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)
--------------------------------------------------------------------------------