├── .github
└── FUNDING.yml
├── .gitignore
├── LICENSE
├── MOS.wsp
├── MOS.zdsproj
├── README.md
├── eZ80F92_AGON_Flash.ztgt
├── main.c
├── src
├── clock.c
├── clock.h
├── config.h
├── defines.h
├── equs.inc
├── gpio.asm
├── i2c.c
├── i2c.h
├── i2c.inc
├── interrupts.asm
├── keyboard.asm
├── macros.inc
├── misc.asm
├── mos.c
├── mos.h
├── mos_api.asm
├── mos_api.inc
├── mos_editor.c
├── mos_editor.h
├── rtc.asm
├── sd.asm
├── sd.h
├── sd.inc
├── serial.asm
├── spi.asm
├── spi.h
├── strings.c
├── strings.h
├── timer.c
├── timer.h
├── uart.c
├── uart.h
└── vdp_protocol.asm
├── src_fatfs
├── LICENSE
├── diskio.c
├── diskio.h
├── ff.c
├── ff.h
├── ffconf.h
├── ffsystem.c
└── ffunicode.c
└── src_startup
├── cstartup.asm
├── globals.asm
├── init_params_f92.asm
└── vectors16.asm
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | ko_fi: breakintoprogram
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | Debug/
2 | Release/
--------------------------------------------------------------------------------
/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.
--------------------------------------------------------------------------------
/MOS.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 |
--------------------------------------------------------------------------------
/MOS.zdsproj:
--------------------------------------------------------------------------------
1 |
2 | eZ80F92
3 |
4 |
5 |
6 | .\main.c
7 | src_startup\globals.asm
8 | src\mos.c
9 | src\mos_editor.c
10 | src\mos_api.asm
11 | src\misc.asm
12 | src\keyboard.asm
13 | src\vdp_protocol.asm
14 | src\interrupts.asm
15 | src\clock.c
16 | src\rtc.asm
17 | src\timer.c
18 | src\sd.asm
19 | src\spi.asm
20 | src_fatfs\diskio.c
21 | src_fatfs\ff.c
22 | src_fatfs\ffsystem.c
23 | src_fatfs\ffunicode.c
24 | src\uart.c
25 | src\serial.asm
26 | src\gpio.asm
27 | src_startup\cstartup.asm
28 | src_startup\init_params_f92.asm
29 | src_startup\vectors16.asm
30 | src\config.h
31 | src\mos_api.inc
32 | src\defines.h
33 | src\i2c.c
34 | src\strings.c
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 |
265 |
266 |
267 |
268 |
269 |
270 |
271 |
272 |
273 |
274 |
275 |
276 |
277 |
278 |
279 |
280 |
281 |
282 |
283 |
284 |
285 |
286 |
287 |
288 |
289 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # agon-mos
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 | ### What is a MOS
12 |
13 | The MOS is a command line machine operating system, similar to CP/M or DOS, that provides a human interface to the Agon file system.
14 |
15 | It also provides an API for file I/O and other common operations for BBC Basic for Z80 and other third-party applications.
16 |
17 | ### Loadig BBC Basic for Z80
18 |
19 | 1. Download bbcbasic.bin from [agon-bbc-basic releases](https://github.com/breakintoprogram/agon-bbc-basic/releases)
20 | 2. Copy it to the root directory of the Agon SD card
21 | 3. Insert the SD card into the AGON and reset/boot it
22 | 4. Check the file is on the SD card with a `CAT` or `.` command
23 | 5. Type the following commands into MOS:
24 | - `LOAD bbcbasic.bin`
25 | - `RUN`
26 | 6. You should then be greeted with the BBC Basic for Z80 prompt
27 |
28 | ### Etiquette
29 |
30 | Please do not issue pull requests or issues for this project; it is very much a work-in-progress.
31 | I will review this policy once the code is approaching live status and I have time to collaborate more.
32 |
33 | ### Build
34 |
35 | The eZ80 is programmed via the ZDI connector on the left-hand side of the board. This requires a Zilog Smart Cable that can be purchased from online stockists such as Mouser or RS Components.
36 |
37 | There are three compatible cables with the following part numbers:
38 |
39 | - `ZUSBSC00100ZACG`: USB Smart Cable (discontinued)
40 | - `ZUSBASC0200ZACG`: USB Smart Cable (in stock - this requires ZDS II version 5.3.5)
41 | - `ZENETSC0100ZACG`: Ethernet Smart Cable (in stock)
42 |
43 | Important! Make sure you get the exact model of cable; there are variants for the Zilog Encore CPU that have similar part numbers, but are not compatible with the Acclaim! eZ80 CPU.
44 |
45 | You can download the ZDS II tools for free via these links. The software contains an IDE, Debugger, C Compiler and eZ80 Assembler.
46 |
47 | - [Zilog ZDS II Tools version 5.3.4](https://zilog.com/index.php?option=com_zcm&task=view&soft_id=38&Itemid=74)
48 | - [Zilog ZDS II Tools version 5.3.5](https://zilog.com/index.php?option=com_zcm&task=view&soft_id=54&Itemid=74)
49 |
50 | You will also need a [Hex2Bin utility](https://sourceforge.net/projects/hex2bin/) to convert the MOS.hex file to MOS.bin ready for release.
51 |
52 | NB:
53 |
54 | - The tools are only available for Windows PC.
55 | - The tools are compatible with Wine on Linux / OSX, but the USB Smart Cable drivers do not work
56 | - The Ethernet Smart Cable may be compatible with the tools running on Wine, though this combination has not been tested by me
57 |
58 | I currently build the official Quark software on a Windows XP KVM running on Ubuntu 20.04 LTS, using ZDS II Tools version 5.3.4, connecting to the Agon with the discontinued USB smart cable.
59 |
60 | It is recommended that building and testing the MOS is done via the ZDS II tools and a Zilog Smart Cable, otherwise there is an increased risk of bricking the Agon.
61 |
62 | Other options for developing C and eZ80 on the Agon are available. Please check the [Agon Programmers Group on Facebook](https://www.facebook.com/groups/667325088311886) for more information.
63 |
64 | Any custom settings for Agon development is contained within the project files, so no further configuration will need to be done.
65 |
66 | ### Documentation
67 |
68 | The AGON documentation can now be found on the [Agon Light Documentation Wiki](https://github.com/breakintoprogram/agon-docs/wiki)
69 |
70 | ### Licenses
71 |
72 | This code is released under an MIT license, with the following exceptions:
73 |
74 | * FatFS: The license for the [FAT filing system by ChaN](http://elm-chan.org/fsw/ff/00index_e.html) can be found here [src_fatfs/LICENSE](src_fatfs/LICENSE) along with the accompanying code.
75 |
76 | ### Links
77 |
78 | - [Zilog eZ80 User Manual](http://www.zilog.com/docs/um0077.pdf)
79 | - [ZiLOG Developer Studio II User Manual](http://www.zilog.com/docs/devtools/um0144.pdf)
80 | - [FatFS - Generic File System](http://elm-chan.org/fsw/ff/00index_e.html)
81 | - [AVRC Tutorials - Initialising an SD Card](http://www.rjhcoding.com/avrc-sd-interface-1.php)
--------------------------------------------------------------------------------
/eZ80F92_AGON_Flash.ztgt:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Oscillator
5 | 18432000
6 |
7 |
8 | 0
9 | C0000
10 | 0
11 | true
12 |
13 |
14 |
15 | 100000
16 | false
17 | 40000
18 | BFFFF
19 |
20 | 1
21 | false
22 | true
23 |
24 |
25 |
26 |
27 | 1
28 | 8
29 | 4
30 | B
31 |
32 |
33 | 0
34 | 08
35 | c0
36 | c7
37 |
38 |
39 | 0
40 | 08
41 | 80
42 | bf
43 |
44 |
45 | 82
46 | 18
47 | 03
48 | 03
49 |
50 |
51 |
52 | 0
53 | b7
54 | true
55 | true
56 |
57 | 1
58 |
59 | EZ80F92
60 | 1.0.1
61 | 1.00
62 |
63 |
--------------------------------------------------------------------------------
/main.c:
--------------------------------------------------------------------------------
1 | /*
2 | * Title: AGON MOS
3 | * Author: Dean Belfield
4 | * Created: 19/06/2022
5 | * Last Updated: 11/11/2023
6 | *
7 | * Modinfo:
8 | * 11/07/2022: Version 0.01: Tweaks for Agon Light, Command Line code added
9 | * 13/07/2022: Version 0.02
10 | * 15/07/2022: Version 0.03: Warm boot support, VBLANK interrupt
11 | * 25/07/2022: Version 0.04; Tweaks to initialisation and interrupts
12 | * 03/08/2022: Version 0.05: Extended MOS for BBC Basic, added config file
13 | * 05/08/2022: Version 0.06: Interim release with hardware flow control enabled
14 | * 10/08/2022: Version 0.07: Bug fixes
15 | * 05/09/2022: Version 0.08: Minor updates to MOS
16 | * 02/10/2022: Version 1.00: Improved error handling for languages, changed bootup title to Quark
17 | * 03/10/2022: Version 1.01: Added SET command, tweaked error handling
18 | * 20/10/2022: + Tweaked error handling
19 | * 13/11/2022: Version 1.02
20 | * 14/03/2023 Version 1.03: SD now uses timer0, does not require interrupt
21 | * + Stubbed command history
22 | * 22/03/2023: + Moved command history to mos_editor.c
23 | * 23/03/2023: RC2 + Increased baud rate to 1152000
24 | * + Improved ESP32->eZ80 boot sync
25 | * 29/03/2023: RC3 + Added UART1 initialisation, tweaked startup sequence timings
26 | * 16/05/2023: Version 1.04: Fixed MASTERCLOCK value in uart.h, added startup beep
27 | * 03/08/2023: RC2 + Enhanced low-level keyboard functionality
28 | * 27/09/2023: + Updated RTC
29 | * 11/11/2023: RC3 + See Github for full list of changes
30 | */
31 |
32 | #include
33 | #include
34 | #include
35 | #include
36 | #include
37 |
38 | #include "defines.h"
39 | #include "config.h"
40 | #include "uart.h"
41 | #include "spi.h"
42 | #include "timer.h"
43 | #include "ff.h"
44 | #include "clock.h"
45 | #include "mos.h"
46 | #include "i2c.h"
47 |
48 | #define MOS_version 1
49 | #define MOS_revision 4
50 | #define MOS_rc 0
51 |
52 | extern void * set_vector(unsigned int vector, void(*handler)(void));
53 |
54 | extern void vblank_handler(void);
55 | extern void uart0_handler(void);
56 | extern void i2c_handler(void);
57 |
58 | extern char coldBoot; // 1 = cold boot, 0 = warm boot
59 | extern volatile char keycode; // Keycode
60 | extern volatile char gp; // General poll variable
61 |
62 | extern volatile BYTE history_no;
63 | extern volatile BYTE history_size;
64 |
65 | static char cmd[256]; // Array for the command line handler
66 |
67 | // Wait for the ESP32 to respond with a GP packet to signify it is ready
68 | // Parameters:
69 | // - pUART: Pointer to a UART structure
70 | // - baudRate: Baud rate to initialise UART with
71 | // Returns:
72 | // - 1 if the function succeeded, otherwise 0
73 | //
74 | int wait_ESP32(UART * pUART, UINT24 baudRate) {
75 | int i, t;
76 |
77 | pUART->baudRate = baudRate; // Initialise the UART object
78 | pUART->dataBits = 8;
79 | pUART->stopBits = 1;
80 | pUART->parity = PAR_NOPARITY;
81 | pUART->flowControl = FCTL_HW;
82 | pUART->interrupts = UART_IER_RECEIVEINT;
83 |
84 | open_UART0(pUART); // Open the UART
85 | init_timer0(10, 16, 0x00); // 10ms timer for delay
86 | gp = 0; // Reset the general poll byte
87 | for(t = 0; t < 200; t++) { // A timeout loop (200 x 50ms = 10s)
88 | putch(23); // Send a general poll packet
89 | putch(0);
90 | putch(VDP_gp);
91 | putch(1);
92 | for(i = 0; i < 5; i++) { // Wait 50ms
93 | wait_timer0();
94 | }
95 | if(gp == 1) break; // If general poll returned, then exit for loop
96 | }
97 | enable_timer0(0); // Disable the timer
98 | return gp;
99 | }
100 |
101 | // Initialise the interrupts
102 | //
103 | void init_interrupts(void) {
104 | set_vector(PORTB1_IVECT, vblank_handler); // 0x32
105 | set_vector(UART0_IVECT, uart0_handler); // 0x18
106 | set_vector(I2C_IVECT, i2c_handler); // 0x1C
107 | }
108 |
109 | // The main loop
110 | //
111 | int main(void) {
112 | UART pUART0;
113 |
114 | DI(); // Ensure interrupts are disabled before we do anything
115 | init_interrupts(); // Initialise the interrupt vectors
116 | init_rtc(); // Initialise the real time clock
117 | init_spi(); // Initialise SPI comms for the SD card interface
118 | init_UART0(); // Initialise UART0 for the ESP32 interface
119 | init_UART1(); // Initialise UART1
120 | EI(); // Enable the interrupts now
121 |
122 | if(!wait_ESP32(&pUART0, 1152000)) { // Try to lock onto the ESP32 at maximum rate
123 | if(!wait_ESP32(&pUART0, 384000)) { // If that fails, then fallback to the lower baud rate
124 | gp = 2; // Flag GP as 2, just in case we need to handle this error later
125 | }
126 | }
127 | if(coldBoot == 0) { // If a warm boot detected then
128 | putch(12); // Clear the screen
129 | }
130 | printf("Agon Quark MOS Version %d.%02d", MOS_version, MOS_revision);
131 | #if MOS_rc > 0
132 | printf(" RC%d", MOS_rc);
133 | #endif
134 | printf("\n\r\n\r");
135 | #if DEBUG > 0
136 | printf("@Baud Rate: %d\n\r\n\r", pUART0.baudRate);
137 | #endif
138 |
139 | mos_mount(); // Mount the SD card
140 |
141 | putch(7); // Startup beep
142 | history_no = 0;
143 | history_size = 0;
144 |
145 | // Load the autoexec.bat config file
146 | //
147 | #if enable_config == 1
148 | if(coldBoot > 0) { // Check it's a cold boot (after reset, not RST 00h)
149 | mos_BOOT("autoexec.txt", cmd, sizeof cmd); // Then load and run the config file
150 | }
151 | #endif
152 |
153 | // The main loop
154 | //
155 | while(1) {
156 | if(mos_input(&cmd, sizeof(cmd)) == 13) {
157 | int err = mos_exec(&cmd);
158 | if(err > 0) {
159 | mos_error(err);
160 | }
161 | }
162 | else {
163 | printf("%cEscape\n\r", MOS_prompt);
164 | }
165 | }
166 |
167 | return 0;
168 | }
169 |
--------------------------------------------------------------------------------
/src/clock.c:
--------------------------------------------------------------------------------
1 | /*
2 | * Title: AGON MOS - Real Time Clock
3 | * Author: Dean Belfield
4 | * Created: 09/03/2023
5 | * Last Updated: 26/09/2023
6 | *
7 | * Modinfo:
8 | * 15/03/2023: Added rtc_getDateString, rtc_update
9 | * 21/03/2023: Uses VDP values from defines.h
10 | * 05/06/2023: Added RTC enable flag
11 | * 26/09/2023: Timestamps now packed into 6 bytes
12 | */
13 |
14 | #include
15 | #include
16 | #include
17 | #include
18 | #include
19 | #include
20 |
21 | #include "defines.h"
22 | #include "uart.h"
23 | #include "clock.h"
24 |
25 | extern volatile BYTE vpd_protocol_flags; // In globals.asm
26 | extern volatile BYTE rtc_enable; // In globals.asm
27 |
28 | const char * rtc_days[7][2] = {
29 | { "Sun", "Sunday" },
30 | { "Mon", "Monday" },
31 | { "Tue", "Tuesday" },
32 | { "Wed", "Wednesday" },
33 | { "Thu", "Thursday" },
34 | { "Fri", "Friday" },
35 | { "Sat", "Saturday" },
36 | };
37 |
38 | const char * rtc_months[12][2] = {
39 | { "Jan", "January" },
40 | { "Feb", "February" },
41 | { "Mar", "March" },
42 | { "Apr", "April" },
43 | { "May", "May" },
44 | { "Jun", "June" },
45 | { "Jul", "July" },
46 | { "Aug", "August" },
47 | { "Sep", "September" },
48 | { "Oct", "October" },
49 | { "Nov", "November" },
50 | { "Dec", "December" },
51 | };
52 |
53 | // Request an update of the RTC from the ESP32
54 | //
55 | void rtc_update() {
56 | if(!rtc_enable) {
57 | return;
58 | }
59 | vpd_protocol_flags &= 0xDF; // Reset bit 5
60 |
61 | putch(23); // Request the time from the ESP32
62 | putch(0);
63 | putch(VDP_rtc);
64 | putch(0); // 0: Get time
65 |
66 | while((vpd_protocol_flags & 0x20) == 0);
67 | }
68 |
69 | // Unpack a 6-byte RTC packet into time struct
70 | // Parameters:
71 | // - buffer: Pointer to the RTC packet data
72 | // - t: Pointer to the time structure to populate
73 | //
74 | void rtc_unpack(UINT8 * buffer, vdp_time_t * t) {
75 | UINT32 d = *(UINT32 *)buffer;
76 |
77 | t->month = (d & 0x0000000F); // uint32_t month : 4; 00000000 00000000 00000000 0000xxxx : 00 00 00 0F >> 0
78 | t->day = (d & 0x000001F0) >> 4; // uint32_t day : 5; 00000000 00000000 0000000x xxxx0000 : 00 00 01 F0 >> 4
79 | t->dayOfWeek = (d & 0x00000E00) >> 9; // uint32_t dayOfWeek : 3; 00000000 00000000 0000xxx0 00000000 : 00 00 0E 00 >> 9
80 | t->dayOfYear = (d & 0x001FF000) >> 12; // uint32_t dayOfYear : 9; 00000000 000xxxxx xxxx0000 00000000 : 00 1F F0 00 >> 12
81 | t->hour = (d & 0x03E00000) >> 21; // uint32_t hour : 5; 000000xx xxx00000 00000000 00000000 : 03 E0 00 00 >> 21
82 | t->minute = (d & 0xFC000000) >> 26; // uint32_t minute : 6; xxxxxx00 00000000 00000000 00000000 : FC 00 00 00 >> 26
83 |
84 | t->second = buffer[4];
85 | t->year = (char)buffer[5] + EPOCH_YEAR ;
86 | }
87 |
88 | // Format a date/time string
89 | //
90 | void rtc_formatDateTime(char * buffer, vdp_time_t * t) {
91 | sprintf(buffer, "%s, %02d/%02d/%4d %02d:%02d:%02d", rtc_days[t->dayOfWeek][0], t->day, t->month + 1, t->year, t->hour, t->minute, t->second);
92 | }
--------------------------------------------------------------------------------
/src/clock.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Title: AGON MOS - Real Time Clock
3 | * Author: Dean Belfield
4 | * Created: 09/03/2023
5 | * Last Updated: 26/09/2023
6 | *
7 | * Modinfo:
8 | * 15/03/2023: Added rtc_getDateString, rtc_update
9 | * 26/09/2023: Timestamps now packed into 6 bytes
10 | */
11 |
12 | #ifndef RTC_H
13 | #define RTC_H
14 |
15 | #define EPOCH_YEAR 1980
16 |
17 | // RTC time structure
18 | //
19 | typedef struct {
20 | UINT16 year;
21 | UINT8 month;
22 | UINT8 day;
23 | UINT8 dayOfWeek;
24 | UINT16 dayOfYear;
25 | UINT8 hour;
26 | UINT8 minute;
27 | UINT8 second;
28 | } vdp_time_t;
29 |
30 | void init_rtc(); // In rtc.asm
31 |
32 | void rtc_update();
33 | void rtc_unpack(UINT8 * buffer, vdp_time_t * t);
34 | void rtc_formatDateTime(char * buffer, vdp_time_t * t);
35 |
36 | #endif RTC_H
37 |
--------------------------------------------------------------------------------
/src/config.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Title: AGON MOS - MOS config
3 | * Author: Dean Belfield
4 | * Created: 19/09/2022
5 | * Last Updated: 13/11/2022
6 | *
7 | * Modinfo:
8 | * 13/11/2022: Added MOS_starLoadAddress
9 | */
10 |
11 | #ifndef CONFIG_H
12 | #define CONFIG_H
13 |
14 | #define enable_config 1 // 0 = disable boot config loading, 1 = enable
15 |
16 | #define MOS_prompt '*' // MOS prompt character
17 | #define MOS_maxOpenFiles 8 // Maximum number of files that mos_FOPEN can open at the same time
18 | #define MOS_defaultLoadAddress 0x040000 // Default load address for LOAD and RUN commands
19 | #define MOS_starLoadAddress 0xB0000 // Address for loading on-SD star commands
20 |
21 | #endif CONFIG_H
--------------------------------------------------------------------------------
/src/defines.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Title: AGON MOS - MOS defines
3 | * Author: Dean Belfield
4 | * Created: 21/03/2023
5 | * Last Updated: 10/11/2023
6 | *
7 | * Modinfo:
8 | * 22/03/2023: The VDP commands are now indexed from 0x80
9 | * 24/03/2023: Added DEBUG
10 | * 10/11/2023: Added VDP_consolemode
11 | */
12 |
13 | #ifndef MOS_DEFINES_H
14 | #define MOS_DEFINES_H
15 |
16 | #define DEBUG 0 // Set to 0 for production, 1 for extra debug information
17 |
18 | // VDP specific (for VDU 23,0,n commands)
19 | //
20 | #define VDP_gp 0x80
21 | #define VDP_keycode 0x81
22 | #define VDP_cursor 0x82
23 | #define VDP_scrchar 0x83
24 | #define VDP_scrpixel 0x84
25 | #define VDP_audio 0x85
26 | #define VDP_mode 0x86
27 | #define VDP_rtc 0x87
28 | #define VDP_keystate 0x88
29 | #define VDP_logicalcoords 0xC0
30 | #define VDP_consolemode 0xFE
31 | #define VDP_terminalmode 0xFF
32 |
33 | #endif MOS_DEFINES_H
--------------------------------------------------------------------------------
/src/equs.inc:
--------------------------------------------------------------------------------
1 | ;
2 | ; Title: AGON MOS - Equs
3 | ; Author: Dean Belfield
4 | ; Created: 15/07/2022
5 | ; Last Updated: 08/06/2023
6 | ;
7 | ; Modinfo:
8 | ; 24/07/2022: Added TMR2_CTL
9 | ; 03/08/2022: Added UART0_BUFFERLEN
10 | ; 20/08/2022: Added some VDP protocol flags
11 | ; 18/09/2022: Added VDPP_FLAG_MODE
12 | ; 09/03/2023: Renamed TMR2_CTL to TMR0_CTL
13 | ; 15/03/2023: Added VDPP_FLAG_RTC
14 | ; 19/03/2023: Fixed TMR0_RR_H to point to correct register
15 | ; 08/06/2023: Add MASTERCLOCK to permit clock delay calculations
16 |
17 | ; System clock speed in Hz
18 | MASTERCLOCK: EQU 18432000
19 |
20 | ; MOS specific
21 | ;
22 | VDPP_BUFFERLEN: EQU 16 ; VDP Protocol Buffer Length
23 |
24 | VDPP_FLAG_CURSOR: EQU 00000001b
25 | VDPP_FLAG_SCRCHAR: EQU 00000010b
26 | VDPP_FLAG_POINT: EQU 00000100b
27 | VDPP_FLAG_AUDIO: EQU 00001000b
28 | VDPP_FLAG_MODE: EQU 00010000b
29 | VDPP_FLAG_RTC: EQU 00100000b
30 | VDPP_FLAG_MOUSE: EQU 01000000b
31 | ; VDPP_FLAG_BUFFERED: EQU 10000000b
32 |
33 | ; For GPIO
34 | ; PA not available on eZ80F92
35 | ;
36 | PA_DR: EQU 96h
37 | PA_DDR: EQU 97h
38 | PA_ALT1: EQU 98h
39 | PA_ALT2: EQU 99h
40 | PB_DR: EQU 9Ah
41 | PB_DDR: EQU 9Bh
42 | PB_ALT1: EQU 9Ch
43 | PB_ALT2: EQU 9Dh
44 | PC_DR: EQU 9Eh
45 | PC_DDR: EQU 9Fh
46 | PC_ALT1: EQU A0h
47 | PC_ALT2: EQU A1h
48 | PD_DR: EQU A2h
49 | PD_DDR: EQU A3h
50 | PD_ALT1: EQU A4h
51 | PD_ALT2: EQU A5h
52 |
53 | GPIOMODE_OUT: EQU 0 ; Output
54 | GPIOMODE_IN: EQU 1 ; Input
55 | GPIOMODE_DIO: EQU 2 ; Open Drain IO
56 | GPIOMODE_SIO: EQU 3 ; Open Source IO
57 | GPIOMODE_INTD: EQU 4 ; Interrupt, Dual Edge
58 | GPIOMODE_ALTF: EQU 5; ; Alt Function
59 | GPIOMODE_INTAL: EQU 6 ; Interrupt, Active Low
60 | GPIOMODE_INTAH: EQU 7 ; Interrupt, Active High
61 | GPIOMODE_INTFE: EQU 8 ; Interrupt, Falling Edge
62 | GPIOMODE_INTRE: EQU 9 ; Interrupt, Rising Edge
63 |
64 | ; For interrupts.asm
65 | ;
66 |
67 | ;UARTs
68 | ;
69 | UART0_IVECT EQU 18h
70 | UART1_IVECT EQU 1Ah
71 |
72 | ;Ports
73 | ;
74 | PB0_IVECT EQU 30h ; AGON ITRP Interrupt (Pin 28/IO17 of the ESP32)
75 | PB1_IVECT EQU 32h ; AGON VBLANK Interrupt (Pin 23/IO15 of the ESP32)
76 | PB2_IVECT EQU 34h
77 | PB3_IVECT EQU 36h
78 | PB4_IVECT EQU 38h
79 | PB5_IVECT EQU 3Ah
80 | PB6_IVECT EQU 3Ch
81 | PB7_IVECT EQU 3Eh
82 |
83 | PC0_IVECT EQU 40h
84 | PC1_IVECT EQU 42h
85 | PC2_IVECT EQU 44h
86 | PC3_IVECT EQU 46h
87 | PC4_IVECT EQU 48h
88 | PC5_IVECT EQU 4Ah
89 | PC6_IVECT EQU 4Ch
90 | PC7_IVECT EQU 4Eh
91 |
92 | PD0_IVECT EQU 50h
93 | PD1_IVECT EQU 52h
94 | PD2_IVECT EQU 54h
95 | PD3_IVECT EQU 56h
96 | PD4_IVECT EQU 58h
97 | PD5_IVECT EQU 5Ah
98 | PD6_IVECT EQU 5Ch
99 | PD7_IVECT EQU 5Eh
100 |
101 | ; For vectors16.asm
102 | ;
103 | TMR0_CTL EQU 80h
104 | TMR0_DR_L EQU 81h
105 | TMR0_RR_L EQU 81h
106 | TMR0_DR_H EQU 82h
107 | TMR0_RR_H EQU 82h
108 |
--------------------------------------------------------------------------------
/src/gpio.asm:
--------------------------------------------------------------------------------
1 | ;
2 | ; Title: AGON MOS - gpio code
3 | ; Author: Dean Belfield
4 | ; Created: 15/07/2022
5 | ; Last Updated: 24/07/2022
6 | ;
7 | ; Modinfo:
8 | ; 24/07/2022: Moved SWITCH_A to misc.asm
9 |
10 | INCLUDE "macros.inc"
11 | INCLUDE "equs.inc"
12 |
13 | .ASSUME ADL = 1
14 |
15 | DEFINE .STARTUP, SPACE = ROM
16 | SEGMENT .STARTUP
17 |
18 | XDEF GPIOB_SETMODE
19 | XREF SWITCH_A
20 |
21 | ; A: Mode
22 | ; B: Pins
23 | ;
24 | GPIOB_SETMODE: CALL SWITCH_A
25 | DW GPIOB_M0 ; Output
26 | DW GPIOB_M1 ; Input
27 | DW GPIOB_M2 ; Open Drain IO
28 | DW GPIOB_M3 ; Open Source IO
29 | DW GPIOB_M4 ; Interrupt, Dual Edge
30 | DW GPIOB_M5 ; Alt Function
31 | DW GPIOB_M6 ; Interrupt, Active Low
32 | DW GPIOB_M7 ; Interrupt, Active High
33 | DW GPIOB_M8 ; Interrupt, Falling Edge
34 | DW GPIOB_M9 ; Interrupt, Rising Edge
35 |
36 | ; Output
37 | ;
38 | GPIOB_M0: RES_GPIO PB_DDR, B
39 | RES_GPIO PB_ALT1, B
40 | RES_GPIO PB_ALT2, B
41 | RET
42 |
43 | ; Input
44 | ;
45 | GPIOB_M1: SET_GPIO PB_DDR, B
46 | RES_GPIO PB_ALT1, B
47 | RES_GPIO PB_ALT2, B
48 | RET
49 |
50 | ; Open Drain IO
51 | ;
52 | GPIOB_M2: RES_GPIO PB_DDR, B
53 | SET_GPIO PB_ALT1, B
54 | RES_GPIO PB_ALT2, B
55 | RET
56 |
57 | ; Open Source IO
58 | ;
59 | GPIOB_M3: SET_GPIO PB_DDR, B
60 | SET_GPIO PB_ALT1, B
61 | RES_GPIO PB_ALT2, B
62 | RET
63 |
64 | ; Interrupt, Dual Edge
65 | ;
66 | GPIOB_M4: SET_GPIO PB_DR, B
67 | RES_GPIO PB_DDR, B
68 | RES_GPIO PB_ALT1, B
69 | RES_GPIO PB_ALT2, B
70 | RET
71 |
72 | ; Alt Function
73 | ;
74 | GPIOB_M5: SET_GPIO PB_DDR, B
75 | RES_GPIO PB_ALT1, B
76 | SET_GPIO PB_ALT2, B
77 | RET
78 |
79 | ; Interrupt, Active Low
80 | ;
81 | GPIOB_M6: RES_GPIO PB_DR, B
82 | RES_GPIO PB_DDR, B
83 | SET_GPIO PB_ALT1, B
84 | SET_GPIO PB_ALT2, B
85 | RET
86 |
87 |
88 | ; Interrupt, Active High
89 | ;
90 | GPIOB_M7: SET_GPIO PB_DR, B
91 | RES_GPIO PB_DDR, B
92 | SET_GPIO PB_ALT1, B
93 | SET_GPIO PB_ALT2, B
94 | RET
95 |
96 |
97 | ; Interrupt, Falling Edge
98 | ;
99 | GPIOB_M8: RES_GPIO PB_DR, B
100 | SET_GPIO PB_DDR, B
101 | SET_GPIO PB_ALT1, B
102 | SET_GPIO PB_ALT2, B
103 | RET
104 |
105 | ; Interrupt, Rising Edge
106 | ;
107 | GPIOB_M9: SET_GPIO PB_DR, B
108 | SET_GPIO PB_DDR, B
109 | SET_GPIO PB_ALT1, B
110 | SET_GPIO PB_ALT2, B
111 | RET
--------------------------------------------------------------------------------
/src/i2c.c:
--------------------------------------------------------------------------------
1 | /*
2 | * Title: AGON MOS - I2C code
3 | * Author: Jeroen Venema
4 | * Created: 07/11/2023
5 | * Last Updated: 10/11/2023
6 | *
7 | * Modinfo:
8 | */
9 |
10 | #include
11 | #include "i2c.h"
12 | #include
13 | #include "timer.h"
14 |
15 | // Set I2C clock and sampling frequency
16 | void I2C_setfrequency(UINT8 id) {
17 | switch(id) {
18 | case(I2C_SPEED_115200):
19 | I2C_CCR = (0x01<<3) | 0x03; // 115.2KHz fast-mode (400KHz max), sampling at 4.6MHz
20 | break;
21 | case(I2C_SPEED_230400):
22 | I2C_CCR = (0x01<<3) | 0x02; // 230.4KHz fast-mode (400KHz max), sampling at 2.3Mhz
23 | break;
24 | case(I2C_SPEED_57600):
25 | default:
26 | I2C_CCR = (0x01<<3) | 0x04; // 57.6KHz default standard-mode (100KHz max), sampling at 1.15Mhz
27 | }
28 | }
29 |
30 | // Initializes the I2C bus
31 | void init_I2C(void) {
32 | i2c_msg_size = 0;
33 | CLK_PPD1 = CLK_PPD_I2C_OFF; // Power Down I2C block before enabling it, avoid locking bug
34 | I2C_CTL = I2C_CTL_ENAB; // Enable I2C block, don't enable interrupts yet
35 | I2C_setfrequency(0);
36 | CLK_PPD1 = 0x0; // Power up I2C block
37 | }
38 |
39 | // Internal function
40 | void I2C_handletimeout(void) {
41 | // reset the interface
42 | I2C_CTL = 0;
43 | init_I2C();
44 | }
45 |
46 | // Open the I2C bus, register the driver interrupt
47 | // Parameters: None
48 | // Returns: None
49 | void mos_I2C_OPEN(UINT8 frequency) {
50 | init_I2C();
51 | I2C_setfrequency(frequency);
52 | }
53 |
54 | // Close the I2C bus, deregister the driver interrupt
55 | // Parameters: None
56 | // Returns: None
57 | void mos_I2C_CLOSE(void) {
58 | CLK_PPD1 = CLK_PPD_I2C_OFF; // Power Down I2C block
59 | I2C_CTL &= ~(I2C_CTL_ENAB); // Disable I2C block
60 | }
61 |
62 | // Write a number of bytes to an address on the I2C bus
63 | // Parameters:
64 | // - i2c_address: I2C address of the slave device
65 | // - size: number of bytes to write
66 | // - buffer: pointer to the first byte to write
67 | // Returns:
68 | // - 0 on success, or errorcode
69 | UINT8 mos_I2C_WRITE(UINT8 i2c_address, UINT8 size, char * buffer) {
70 |
71 | // send maximum of 32 bytes in a single I2C transaction
72 | if(size > I2C_MAX_BUFFERLENGTH) size = I2C_MAX_BUFFERLENGTH;
73 | if(i2c_address > 127) return RET_NORESPONSE;
74 | // wait for IDLE status
75 | init_timer0(1, 16, 0x00); // 1ms timer for delay
76 | enable_timer0(1);
77 | while(i2c_role) {
78 | // anything but IDLE (00)
79 | if(get_timer0() > I2C_TIMEOUTMS) {
80 | I2C_handletimeout();
81 | enable_timer0(0); // Disable timer
82 | return RET_ARB_LOST;
83 | }
84 | }
85 | enable_timer0(0);
86 |
87 | i2c_msg_ptr = buffer;
88 | i2c_msg_size = size;
89 | i2c_role = I2C_MTX; // MTX - Master Transmit Mode
90 | i2c_error = RET_OK;
91 | i2c_slave_rw = i2c_address << 1; // shift one bit left, 0 on bit 0 == write action on I2C
92 |
93 | I2C_CTL = I2C_CTL_IEN | I2C_CTL_ENAB | I2C_CTL_STA; // send start condition
94 |
95 | init_timer0(1, 16, 0x00); // 1ms timer for delay
96 | enable_timer0(1);
97 | while(i2c_role == I2C_MTX) { // while MTX
98 | if(get_timer0() > I2C_TIMEOUTMS) {
99 | I2C_handletimeout();
100 | enable_timer0(0); // Disable timer
101 | return RET_DATA_NACK;
102 | }
103 | }
104 |
105 | enable_timer0(0); // Disable timer
106 | return i2c_error;
107 | }
108 |
109 | // Read a number of bytes from an i2c_address on the I2C bus
110 | // Parameters:
111 | // - i2c_address: I2C address of the slave device
112 | // - size: number of bytes to read
113 | // - buffer: pointer to the first byte to read
114 | // Returns:
115 | // - 0 on success, or errorcode
116 | UINT8 mos_I2C_READ(UINT8 i2c_address, UINT8 size, char* buffer)
117 | {
118 | if(size == 0) return 0;
119 | if(i2c_address > 127) return RET_NORESPONSE;
120 | // receive maximum of 32 bytes in a single I2C transaction
121 | if(size > I2C_MAX_BUFFERLENGTH) size = I2C_MAX_BUFFERLENGTH;
122 |
123 | // wait for IDLE status
124 | init_timer0(1, 16, 0x00); // 1ms timer for delay
125 | enable_timer0(1);
126 | while(i2c_role) {
127 | // anything but IDLE (00)
128 | if(get_timer0() > I2C_TIMEOUTMS) {
129 | I2C_handletimeout();
130 | enable_timer0(0); // Disable timer
131 | return RET_ARB_LOST;
132 | }
133 | }
134 | enable_timer0(0);
135 | i2c_msg_ptr = buffer;
136 | i2c_msg_size = size;
137 | i2c_role = I2C_MRX; // MRX mode
138 | i2c_error = RET_OK;
139 | i2c_slave_rw = (1<<0); // receive bit 0
140 | i2c_slave_rw |= i2c_address << 1; // shift 7-bit address one bit left
141 |
142 | I2C_CTL = I2C_CTL_IEN | I2C_CTL_ENAB | I2C_CTL_STA; // send start condition
143 |
144 | init_timer0(1, 16, 0x00); // 1ms timer for delay
145 | enable_timer0(1);
146 | while(i2c_role == I2C_MRX) {
147 | if(get_timer0() > I2C_TIMEOUTMS) {
148 | I2C_handletimeout();
149 | enable_timer0(0); // Disable timer
150 | return RET_ARB_LOST;
151 | }
152 | }
153 | enable_timer0(0); // Disable timer
154 | return i2c_error;
155 | }
156 |
--------------------------------------------------------------------------------
/src/i2c.h:
--------------------------------------------------------------------------------
1 | #ifndef _I2C_H_
2 | #define _I2C_H_
3 |
4 | #include
5 |
6 |
7 | extern volatile char i2c_slave_rw;
8 | extern volatile char i2c_error;
9 | extern volatile char i2c_role;
10 | extern volatile UINT8 i2c_msg_size;
11 | extern volatile char * i2c_msg_ptr;
12 |
13 | // I2C_CTL register bits
14 | #define I2C_CTL_IEN (1<<7)
15 | #define I2C_CTL_ENAB (1<<6)
16 | #define I2C_CTL_STA (1<<5)
17 | #define I2C_CTL_STP (1<<4)
18 | #define I2C_CTL_IFLG (1<<3)
19 | #define I2C_CTL_AAK (1<<2)
20 |
21 | // ez80 PPD register bits
22 | #define CLK_PPD_I2C_OFF (1<<2)
23 |
24 | // I2C return codes to caller
25 | #define RET_OK 0x00
26 | #define RET_NORESPONSE 0x01
27 | #define RET_DATA_NACK 0x02
28 | #define RET_ARB_LOST 0x04
29 | #define RET_BUS_ERROR 0x08
30 |
31 | // I2C constants
32 | #define I2C_MAX_BUFFERLENGTH 32
33 | #define I2C_TIMEOUTMS 2000
34 | #define I2C_SPEED_57600 0x01
35 | #define I2C_SPEED_115200 0x02
36 | #define I2C_SPEED_230400 0x03
37 |
38 | // I2C role state
39 | #define I2C_IDLE 0x00
40 | #define I2C_MTX 0x01
41 | #define I2C_MRX 0x02
42 | #define I2C_SRX 0x04
43 | #define I2C_STX 0x08
44 |
45 | void init_I2C(void);
46 | void mos_I2C_OPEN(UINT8 frequency);
47 | void mos_I2C_CLOSE(void);
48 | UINT8 mos_I2C_WRITE(UINT8 i2c_address, UINT8 size, char * buffer);
49 | UINT8 mos_I2C_READ(UINT8 i2c_address, UINT8 size, char * buffer);
50 |
51 | #endif _I2C_H_
--------------------------------------------------------------------------------
/src/i2c.inc:
--------------------------------------------------------------------------------
1 | ;* Return status codes to read/write caller
2 | RET_OK .equ 00h ; ok - caller sets this at entry
3 | RET_NORESPONSE .equ 01h ; address sent, nack received
4 | RET_DATA_NACK .equ 02h ; data sent, nack received
5 | RET_ARB_LOST .equ 04h ; arbitration lost
6 | RET_BUS_ERROR .equ 08h ; Bus error
7 |
8 | ;* I2C ROLE STATUS
9 | I2C_IDLE .equ 00h
10 | I2C_MTX .equ 01h
11 | I2C_MRX .equ 02h
12 | I2C_SRX .equ 04h
13 | I2C_STX .equ 08h
14 |
15 | ;* I2C_CTL bits
16 | I2C_CTL_IEN .equ 10000000b
17 | I2C_CTL_ENAB .equ 01000000b
18 | I2C_CTL_STA .equ 00100000b
19 | I2C_CTL_STP .equ 00010000b
20 | I2C_CTL_IFLG .equ 00001000b
21 | I2C_CTL_AAK .equ 00000100b
22 |
--------------------------------------------------------------------------------
/src/interrupts.asm:
--------------------------------------------------------------------------------
1 | ;
2 | ; Title: AGON MOS - Interrupt handlers
3 | ; Author: Dean Belfield
4 | ; Created: 03/08/2022
5 | ; Last Updated: 29/03/2023
6 | ;
7 | ; Modinfo:
8 | ; 09/03/2023: No longer uses timer interrupt 0 for SD card timing
9 | ; 29/03/2023: Added support for UART1
10 | ; 10/11/2023: Added support for I2C
11 |
12 | INCLUDE "macros.inc"
13 | INCLUDE "equs.inc"
14 | INCLUDE "ez80f92.inc"
15 | INCLUDE "i2c.inc"
16 |
17 | .ASSUME ADL = 1
18 |
19 | DEFINE .STARTUP, SPACE = ROM
20 | SEGMENT .STARTUP
21 |
22 | XDEF _vblank_handler
23 | XDEF _uart0_handler
24 | XDEF _i2c_handler
25 |
26 | XREF _clock
27 | XREF _vdp_protocol_data
28 |
29 | XREF UART0_serial_RX
30 | XREF UART0_serial_TX
31 | XREF mos_api
32 | XREF vdp_protocol
33 |
34 | XREF _i2c_slave_rw
35 | XREF _i2c_error
36 | XREF _i2c_role
37 | XREF _i2c_msg_ptr
38 | XREF _i2c_msg_size
39 |
40 | ; AGON Vertical Blank Interrupt handler
41 | ;
42 | _vblank_handler: DI
43 | PUSH AF
44 | SET_GPIO PB_DR, 2 ; Need to set this to 2 for the interrupt to work correctly
45 | PUSH BC
46 | PUSH DE
47 | PUSH HL
48 | LD HL, (_clock) ; Increment the 32-bit clock counter
49 | LD BC, 2 ; By 2, effectively timing in centiseconds
50 | ADD HL, BC
51 | LD (_clock), HL
52 | LD A, (_clock + 3)
53 | ADC A, 0
54 | LD (_clock + 3), A
55 | POP HL
56 | POP DE
57 | POP BC
58 | POP AF
59 | EI
60 | RETI.L
61 |
62 | ; AGON UART0 Interrupt Handler
63 | ;
64 | _uart0_handler: DI
65 | PUSH AF
66 | PUSH BC
67 | PUSH DE
68 | PUSH HL
69 | CALL UART0_serial_RX
70 | LD C, A
71 | LD HL, _vdp_protocol_data
72 | CALL vdp_protocol
73 | POP HL
74 | POP DE
75 | POP BC
76 | POP AF
77 | EI
78 | RETI.L
79 |
80 | ; AGON I2C Interrupt handler
81 | ;
82 | _i2c_handler:
83 | DI
84 | PUSH AF
85 | PUSH HL
86 | PUSH DE
87 | IN0 A, (I2C_SR) ; input I2C status register - switch case to this status value
88 | AND 11111000b ; cancel lower 3 bits, so we can use RRA. This will save 1 T-state
89 | RRA
90 | RRA
91 | RRA ; bits [7:3] contain the jumpvector
92 | CALL i2c_handle_sr_vector
93 | ; and switch on the vectors in this table
94 | DW i2c_case_buserror ; 00h
95 | DW i2c_case_master_start ; 08h
96 | DW i2c_case_invalid ; 10h
97 | DW i2c_case_aw_acked ; 18h
98 | DW i2c_case_aw_nacked ; 20h
99 | DW i2c_case_db_acked ; 28h
100 | DW i2c_case_db_nacked ; 30h
101 | DW i2c_case_arblost ; 38h
102 | DW i2c_case_mr_ar_ack ; 40h
103 | DW i2c_case_mr_ar_nack ; 48h
104 | DW i2c_case_mr_dbr_ack ; 50h
105 | DW i2c_case_mr_dbr_nack ; 58h
106 | DW i2c_case_toimplement ; 60h - slave
107 | DW i2c_case_toimplement ; 68h - slave
108 | DW i2c_case_toimplement ; 70h - slave
109 | DW i2c_case_toimplement ; 78h - slave
110 | DW i2c_case_toimplement ; 80h - slave
111 | DW i2c_case_toimplement ; 88h - slave
112 | DW i2c_case_toimplement ; 90h - slave
113 | DW i2c_case_toimplement ; 98h - slave
114 | DW i2c_case_toimplement ; A0h - slave
115 | DW i2c_case_toimplement ; A8h - slave
116 | DW i2c_case_toimplement ; B0h - slave
117 | DW i2c_case_toimplement ; B8h - slave
118 | DW i2c_case_toimplement ; C0h - slave
119 | DW i2c_case_toimplement ; C8h - slave
120 | DW i2c_case_toimplement ; D0h - 10bit I2C address
121 | DW i2c_case_toimplement ; D8h - 10bit I2C address
122 | DW i2c_case_invalid ; E0h
123 | DW i2c_case_invalid ; E8h
124 | DW i2c_case_invalid ; F0h
125 | DW i2c_case_invalid ; F8h - Should never produce an interrupt
126 |
127 | i2c_handle_sr_vector:
128 | EX (SP), HL ; Swap HL with the contents of the top of the stack
129 | ADD A, A ; Multiply A by two
130 | ADD A, L
131 | LD L, A
132 | ADC A, H
133 | SUB L
134 | LD H, A ; Add 8bit A to HL
135 | LD A, (HL) ; Fetch vector adress from the table
136 | INC HL
137 | LD H, (HL)
138 | LD L, A
139 | EX (SP), HL ; Swap this new address back, restores HL
140 | RET ; Return program control to this new address
141 |
142 | i2c_case_buserror: ; 00h
143 | LD A, RET_BUS_ERROR
144 | LD (_i2c_error),A
145 |
146 | ; perform software reset of the bus
147 | XOR A
148 | OUT0 (I2C_SRR),A
149 | LD HL, _i2c_role
150 | LD A, I2C_IDLE ; READY state
151 | LD (HL),A
152 |
153 | POP DE
154 | POP HL
155 | POP AF
156 | EI
157 | RETI.L
158 |
159 | i2c_case_master_start: ; 08h
160 | i2c_case_master_repstart: ; 10h
161 | LD A, (_i2c_slave_rw) ; load slave address and r/w bit
162 | OUT0 (I2C_DR), A ; store to I2C Data Register
163 | LD A, I2C_CTL_IEN | I2C_CTL_ENAB | I2C_CTL_AAK
164 | OUT0 (I2C_CTL),A ; set to Control register
165 |
166 | POP DE
167 | POP HL
168 | POP AF
169 | EI
170 | RETI.L
171 |
172 | i2c_case_aw_acked: ; 18h
173 | i2c_case_db_acked: ; 28h
174 | ; Check size and size--
175 | LD A, (_i2c_msg_size)
176 | OR A
177 | JR Z, i2c_sendstop
178 | DEC A
179 | LD (_i2c_msg_size), A
180 |
181 | ; load pointer
182 | LD HL, _i2c_msg_ptr
183 | LD DE, HL
184 | LD HL, (HL)
185 |
186 | ; Load indexed byte from buffer
187 | LD A, (HL)
188 |
189 | OUT0 (I2C_DR), A ; store to I2C Data Register
190 | LD A, I2C_CTL_IEN | I2C_CTL_ENAB | I2C_CTL_AAK
191 | OUT0 (I2C_CTL),A ; set to Control register
192 |
193 | INC HL ; pointer++
194 | EX DE, HL
195 | LD (HL), DE
196 |
197 | POP DE
198 | POP HL
199 | POP AF
200 | EI
201 | RETI.L
202 |
203 | i2c_case_aw_nacked: ; 20h
204 | i2c_case_mr_ar_nack: ; 48h
205 | LD A, RET_NORESPONSE
206 | LD (_i2c_error),A
207 | JP i2c_sendstop
208 |
209 | i2c_case_db_nacked: ; 30h
210 | LD A, RET_DATA_NACK
211 | LD (_i2c_error),A
212 | JP i2c_sendstop
213 |
214 | i2c_case_arblost: ; 38h
215 | LD A, RET_ARB_LOST
216 | LD (_i2c_error),A
217 | JP i2c_sendstop
218 |
219 | i2c_case_mr_dbr_ack: ; 50h
220 | ; calculate offset address into i2c_mbuffer
221 | LD HL, _i2c_msg_ptr
222 | LD DE, HL
223 | LD HL, (HL)
224 |
225 | IN0 A,(I2C_DR) ; load byte from I2C Data Register
226 | LD (HL),A ; store in buffer at calculated index
227 |
228 | INC HL ; pointer++
229 | EX DE, HL
230 | LD (HL), DE
231 | ; ; intentionally falling through to next case
232 | i2c_case_mr_ar_ack: ; 40h
233 | LD A, (_i2c_msg_size)
234 | CP 1 ; last byte to receive?
235 | JR Z, $F
236 |
237 | LD A, I2C_CTL_IEN | I2C_CTL_ENAB | I2C_CTL_AAK ; reply with ACK
238 | JR $end
239 | $$:
240 | LD A, I2C_CTL_IEN | I2C_CTL_ENAB ; reply without ACK
241 | $end: OUT0 (I2C_CTL),A ; set to Control register
242 | ; size--
243 | LD A, (_i2c_msg_size)
244 | DEC A
245 | LD (_i2c_msg_size), A
246 |
247 | POP DE
248 | POP HL
249 | POP AF
250 | EI
251 | RETI.L
252 |
253 | i2c_case_mr_dbr_nack: ; 58h
254 | ; load pointer
255 | LD HL, _i2c_msg_ptr
256 | LD HL, (HL)
257 |
258 | IN0 A,(I2C_DR) ; load byte from I2C Data Register
259 | LD (HL),A ; store in buffer at calculated index
260 |
261 | JR i2c_sendstop
262 |
263 | i2c_case_invalid:
264 | i2c_case_toimplement:
265 | i2c_sendstop:
266 | ; send stop first to go to idle state
267 | LD A, I2C_CTL_ENAB | I2C_CTL_STP
268 | OUT0 (I2C_CTL),A ; set to Control register
269 | $$:
270 | IN0 A,(I2C_CTL)
271 | AND I2C_CTL_STP ; STP bit still in place?
272 | JR NZ, $B
273 |
274 | ; Release control of the bus
275 | XOR A ; all bits of I2C_CTL to 0
276 | OUT0 (I2C_CTL),A ; set to Control register I2C_CTL
277 | LD HL, _i2c_role
278 | LD A, I2C_IDLE ; IDLE state
279 | LD (HL),A
280 |
281 | POP DE
282 | POP HL
283 | POP AF
284 | EI
285 | RETI.L
286 |
287 | END
288 |
--------------------------------------------------------------------------------
/src/keyboard.asm:
--------------------------------------------------------------------------------
1 | ;
2 | ; Title: AGON MOS - Keyboard routines
3 | ; Author: Dean Belfield
4 | ; Created: 13/08/2023
5 | ; Last Updated: 13/08/2023
6 | ;
7 | ; Modinfo:
8 | INCLUDE "macros.inc"
9 | INCLUDE "equs.inc"
10 |
11 | .ASSUME ADL = 1
12 |
13 | DEFINE .STARTUP, SPACE = ROM
14 | SEGMENT .STARTUP
15 |
16 | XDEF keyboard_handler
17 |
18 | XREF _keymods ; In globals.asm
19 | XREF _keyascii
20 | XREF _keycode
21 | XREF _keymap
22 |
23 | ; Agon keyboard routines
24 | ; C: Keydown state (1 = pressed 0 = depressed)
25 | ; B: Virtual keycode
26 | ;
27 | keyboard_handler: LD A, B
28 | OR A
29 | CALL NZ, keyboard_map
30 | JP keyboard_reset
31 |
32 | ; Write key up and down states to the keymap
33 | ; C: Keydown state (1 = pressed 0 = depressed)
34 | ; A: Virtual keycode
35 | ;
36 | keyboard_map: DEC A ; A: Virtual keycode - 1
37 | LD DE, keyboard_lookup ; DE: Address of keyboard lookup table
38 | LD HL, 0
39 | LD L, A
40 | ADD HL, HL
41 | ADD HL, HL ; HL: Virtual keycode x 4
42 | ADD HL, DE ; HL: Address of key data in keyboard lookup
43 | LD A, (HL) ; A: Bit to set
44 | OR A ; Check for empty entry
45 | RET Z ; Yep, nothing to set here, so return
46 | INC HL
47 | LD HL, (HL) ; HL: Address to set into
48 | BIT 0, C ; Is the key down or up?
49 | JR NZ, $F ; It is down so jump to the code that sets a bit
50 | CPL ; It is up so complement the bit
51 | AND (HL) ; Then AND it with the keymap entry
52 | LD (HL), A
53 | RET
54 | ;
55 | $$: OR (HL) ; OR with the keymap entry
56 | LD (HL), A
57 | RET
58 | ;
59 | ; Now check for CTRL+ALT+DEL
60 | ;
61 | keyboard_reset: LD A, B ; Get the virtual key code
62 | CP 130 ; Check for DEL (cursor keys)
63 | JR Z, $F
64 | CP 131 ; Check for DEL (no numlock)
65 | JR Z , $F
66 | CP 88 ; And DEL (numlock)
67 | RET NZ
68 | $$: LD A, (_keymods) ; DEL is pressed so check CTRL + ALT
69 | AND 05h ; Bit 0 and 2
70 | CP 05h
71 | RET NZ ; Exit if not pressed
72 | ;
73 | ; Here we're just waiting for the key to go up
74 | ;
75 | LD A, C ; Get key down
76 | DEC A ; Check for 0 (key up)
77 | JP NZ, 0 ; Yes so reset
78 | LD (_keyascii), A ; Otherwise clear the keycode so no interaction with console
79 | LD (_keycode), A
80 | RET
81 |
82 | ; Lookup table to convert virtual keycodes to physical keys (BBC BASIC specification)
83 | ; Uses the macro KEY to store each key as an index and a bit
84 | ; Four bytes of data per key
85 | ; - 1 byte: The bit data to set into the keymap
86 | ; - 3 bytes: The address to set (precalculated)
87 | ;
88 | KEY: MACRO VKEYCODE
89 | IF VKEYCODE = 0
90 | DL 0
91 | ELSE
92 | DB 1 << ((VKEYCODE - 1) & 00000111b)
93 | DW24 _keymap + (((VKEYCODE - 1) & 11111000b) >> 3)
94 | ENDIF
95 | ENDMACRO
96 | ;
97 | keyboard_lookup: KEY(099) ; VK_SPACE
98 | KEY(040) ; VK_0
99 | KEY(049) ; VK_1
100 | KEY(050) ; VK_2
101 | KEY(018) ; VK_3
102 | KEY(019) ; VK_4
103 | KEY(020) ; VK_5
104 | KEY(053) ; VK_6
105 | KEY(037) ; VK_7
106 | KEY(022) ; VK_8
107 | KEY(039) ; VK_9
108 | KEY(107) ; VK_KP_0
109 | KEY(108) ; VK_KP_1
110 | KEY(125) ; VK_KP_2
111 | KEY(109) ; VK_KP_3
112 | KEY(123) ; VK_KP_4
113 | KEY(124) ; VK_KP_5
114 | KEY(027) ; VK_KP_6
115 | KEY(028) ; VK_KP_7
116 | KEY(043) ; VK_KP_8
117 | KEY(044) ; VK_KP_9
118 | KEY(066) ; VK_a
119 | KEY(101) ; VK_b
120 | KEY(083) ; VK_c
121 | KEY(051) ; VK_d
122 | KEY(035) ; VK_e
123 | KEY(068) ; VK_f
124 | KEY(084) ; VK_g
125 | KEY(085) ; VK_h
126 | KEY(038) ; VK_i
127 | KEY(070) ; VK_j
128 | KEY(071) ; VK_k
129 | KEY(087) ; VK_l
130 | KEY(102) ; VK_m
131 | KEY(086) ; VK_n
132 | KEY(055) ; VK_o
133 | KEY(056) ; VK_p
134 | KEY(017) ; VK_q
135 | KEY(052) ; VK_r
136 | KEY(082) ; VK_s
137 | KEY(036) ; VK_t
138 | KEY(054) ; VK_u
139 | KEY(100) ; VK_v
140 | KEY(034) ; VK_w
141 | KEY(067) ; VK_x
142 | KEY(069) ; VK_y
143 | KEY(098) ; VK_z
144 | KEY(066) ; VK_A
145 | KEY(101) ; VK_B
146 | KEY(083) ; VK_C
147 | KEY(051) ; VK_D
148 | KEY(035) ; VK_E
149 | KEY(068) ; VK_F
150 | KEY(084) ; VK_G
151 | KEY(085) ; VK_H
152 | KEY(038) ; VK_I
153 | KEY(070) ; VK_J
154 | KEY(071) ; VK_K
155 | KEY(087) ; VK_L
156 | KEY(102) ; VK_M
157 | KEY(086) ; VK_N
158 | KEY(055) ; VK_O
159 | KEY(056) ; VK_P
160 | KEY(017) ; VK_Q
161 | KEY(052) ; VK_R
162 | KEY(082) ; VK_S
163 | KEY(036) ; VK_T
164 | KEY(054) ; VK_U
165 | KEY(100) ; VK_V
166 | KEY(034) ; VK_W
167 | KEY(067) ; VK_X
168 | KEY(069) ; VK_Y
169 | KEY(098) ; VK_Z
170 | KEY(046) ; VK_GRAVEACCENT
171 | KEY(000) ; VK_ACUTEACCENT
172 | KEY(000) ; VK_QUOTE
173 | KEY(000) ; VK_QUOTEDBL
174 | KEY(094) ; VK_EQUALS
175 | KEY(024) ; VK_MINUS
176 | KEY(060) ; VK_KP_MINUS
177 | KEY(094) ; VK_PLUS
178 | KEY(059) ; VK_KP_PLUS
179 | KEY(092) ; VK_KP_MULTIPLY
180 | KEY(000) ; VK_ASTERISK
181 | KEY(000) ; VK_BACKSLASH
182 | KEY(075) ; VK_KP_DIVIDE
183 | KEY(105) ; VK_SLASH
184 | KEY(077) ; VK_KP_PERIOD
185 | KEY(104) ; VK_PERIOD
186 | KEY(073) ; VK_COLON
187 | KEY(103) ; VK_COMMA
188 | KEY(088) ; VK_SEMICOLON
189 | KEY(000) ; VK_AMPERSAND
190 | KEY(000) ; VK_VERTICALBAR
191 | KEY(000) ; VK_HASH
192 | KEY(072) ; VK_AT
193 | KEY(025) ; VK_CARET
194 | KEY(000) ; VK_DOLLAR
195 | KEY(000) ; VK_POUND
196 | KEY(000) ; VK_EURO
197 | KEY(000) ; VK_PERCENT
198 | KEY(000) ; VK_EXCLAIM
199 | KEY(000) ; VK_QUESTION
200 | KEY(000) ; VK_LEFTBRACE
201 | KEY(000) ; VK_RIGHTBRACE
202 | KEY(057) ; VK_LEFTBRACKET
203 | KEY(089) ; VK_RIGHTBRACKET
204 | KEY(000) ; VK_LEFTPAREN
205 | KEY(000) ; VK_RIGHTPAREN
206 | KEY(103) ; VK_LESS
207 | KEY(104) ; VK_GREATER
208 | KEY(096) ; VK_UNDERSCORE
209 | KEY(000) ; VK_DEGREE
210 | KEY(000) ; VK_SECTION
211 | KEY(046) ; VK_TILDE
212 | KEY(024) ; VK_NEGATION
213 | KEY(004) ; VK_LSHIFT
214 | KEY(007) ; VK_RSHIFT
215 | KEY(006) ; VK_LALT
216 | KEY(009) ; VK_RALT
217 | KEY(005) ; VK_LCTRL
218 | KEY(008) ; VK_RCTRL
219 | KEY(126) ; VK_LGUI
220 | KEY(127) ; VK_RGUI
221 | KEY(113) ; VK_ESCAPE
222 | KEY(033) ; VK_PRINTSCREEN
223 | KEY(000) ; VK_SYSREQ
224 | KEY(062) ; VK_INSERT
225 | KEY(062) ; VK_KP_INSERT
226 | KEY(090) ; VK_DELETE
227 | KEY(076) ; VK_KP_DELETE
228 | KEY(048) ; VK_BACKSPACE
229 | KEY(063) ; VK_HOME
230 | KEY(063) ; VK_KP_HOME
231 | KEY(106) ; VK_END
232 | KEY(106) ; VK_KP_END
233 | KEY(000) ; VK_PAUSE
234 | KEY(045) ; VK_BREAK
235 | KEY(032) ; VK_SCROLLLOCK
236 | KEY(078) ; VK_NUMLOCK
237 | KEY(065) ; VK_CAPSLOCK
238 | KEY(097) ; VK_TAB
239 | KEY(074) ; VK_RETURN
240 | KEY(061) ; VK_KP_ENTER
241 | KEY(128) ; VK_APPLICATION
242 | KEY(064) ; VK_PAGEUP
243 | KEY(064) ; VK_KP_PAGEUP
244 | KEY(079) ; VK_PAGEDOWN
245 | KEY(079) ; VK_KP_PAGEDOWN
246 | KEY(058) ; VK_UP
247 | KEY(058) ; VK_KP_UP
248 | KEY(042) ; VK_DOWN
249 | KEY(042) ; VK_KP_DOWN
250 | KEY(026) ; VK_LEFT
251 | KEY(026) ; VK_KP_LEFT
252 | KEY(122) ; VK_RIGHT
253 | KEY(122) ; VK_KP_RIGHT
254 | KEY(000) ; VK_KP_CENTER
255 | KEY(114) ; VK_F1
256 | KEY(115) ; VK_F2
257 | KEY(116) ; VK_F3
258 | KEY(021) ; VK_F4
259 | KEY(117) ; VK_F5
260 | KEY(118) ; VK_F6
261 | KEY(023) ; VK_F7
262 | KEY(119) ; VK_F8
263 | KEY(120) ; VK_F9
264 | KEY(031) ; VK_F10
265 | KEY(029) ; VK_F11
266 | KEY(030) ; VK_F12
267 | KEY(000) ; VK_GRAVE_a
268 | KEY(000) ; VK_GRAVE_e
269 | KEY(000) ; VK_GRAVE_i
270 | KEY(000) ; VK_GRAVE_o
271 | KEY(000) ; VK_GRAVE_u
272 | KEY(000) ; VK_GRAVE_y
273 | KEY(000) ; VK_ACUTE_a
274 | KEY(000) ; VK_ACUTE_e
275 | KEY(000) ; VK_ACUTE_i
276 | KEY(000) ; VK_ACUTE_o
277 | KEY(000) ; VK_ACUTE_u
278 | KEY(000) ; VK_ACUTE_y
279 | KEY(000) ; VK_GRAVE_A
280 | KEY(000) ; VK_GRAVE_E
281 | KEY(000) ; VK_GRAVE_I
282 | KEY(000) ; VK_GRAVE_O
283 | KEY(000) ; VK_GRAVE_U
284 | KEY(000) ; VK_GRAVE_Y
285 | KEY(000) ; VK_ACUTE_A
286 | KEY(000) ; VK_ACUTE_E
287 | KEY(000) ; VK_ACUTE_I
288 | KEY(000) ; VK_ACUTE_O
289 | KEY(000) ; VK_ACUTE_U
290 | KEY(000) ; VK_ACUTE_Y
291 | KEY(000) ; VK_UMLAUT_a
292 | KEY(000) ; VK_UMLAUT_e
293 | KEY(000) ; VK_UMLAUT_i
294 | KEY(000) ; VK_UMLAUT_o
295 | KEY(000) ; VK_UMLAUT_u
296 | KEY(000) ; VK_UMLAUT_y
297 | KEY(000) ; VK_UMLAUT_A
298 | KEY(000) ; VK_UMLAUT_E
299 | KEY(000) ; VK_UMLAUT_I
300 | KEY(000) ; VK_UMLAUT_O
301 | KEY(000) ; VK_UMLAUT_U
302 | KEY(000) ; VK_UMLAUT_Y
303 | KEY(000) ; VK_CARET_a
304 | KEY(000) ; VK_CARET_e
305 | KEY(000) ; VK_CARET_i
306 | KEY(000) ; VK_CARET_o
307 | KEY(000) ; VK_CARET_u
308 | KEY(000) ; VK_CARET_y
309 | KEY(000) ; VK_CARET_A
310 | KEY(000) ; VK_CARET_E
311 | KEY(000) ; VK_CARET_I
312 | KEY(000) ; VK_CARET_O
313 | KEY(000) ; VK_CARET_U
314 | KEY(000) ; VK_CARET_Y
315 | KEY(000) ; VK_CEDILLA_c
316 | KEY(000) ; VK_CEDILLA_C
317 | KEY(000) ; VK_TILDE_a
318 | KEY(000) ; VK_TILDE_o
319 | KEY(000) ; VK_TILDE_n
320 | KEY(000) ; VK_TILDE_A
321 | KEY(000) ; VK_TILDE_O
322 | KEY(000) ; VK_TILDE_N
323 | KEY(000) ; VK_UPPER_a
324 | KEY(000) ; VK_ESZETT
325 | KEY(000) ; VK_EXCLAIM_INV
326 | KEY(000) ; VK_QUESTION_INV
327 | KEY(000) ; VK_INTERPUNCT
328 | KEY(000) ; VK_DIAERESIS
329 | KEY(000) ; VK_SQUARE
330 | KEY(000) ; VK_CURRENCY
331 | KEY(000) ; VK_MU
332 | KEY(000) ; VK_aelig
333 | KEY(000) ; VK_oslash
334 | KEY(000) ; VK_aring
335 | KEY(000) ; VK_AELIG
336 | KEY(000) ; VK_OSLASH
337 | KEY(000) ; VK_ARING
338 | KEY(000) ; VK_YEN
339 | KEY(000) ; VK_MUHENKAN
340 | KEY(000) ; VK_HENKAN
341 | KEY(000) ; VK_KATAKANA_HIRAGANA_ROMAJI
342 | KEY(000) ; VK_HANKAKU_ZENKAKU_KANJI
343 | KEY(000) ; VK_SHIFT_0
344 | KEY(000) ; VK_ASCII
--------------------------------------------------------------------------------
/src/macros.inc:
--------------------------------------------------------------------------------
1 | ;
2 | ; Title: AGON MOS - Useful Macros
3 | ; Author: Dean Belfield
4 | ; Created: 15/07/2022
5 | ; Last Updated: 08/06/2023
6 | ;
7 | ; Modinfo:
8 | ; 08/08/2022: Added GET_GPIO
9 | ; 09/09/2022: Commented
10 | ; 08/06/2023: Add timer-related macros
11 |
12 | ; Used by macros
13 | INCLUDE "equs.inc"
14 |
15 | ; References to functions called by Macros
16 | XREF _timer0_delay
17 |
18 |
19 | ; Add A to HL (unsigned)
20 | ;
21 | ADD8U_HL: MACRO
22 | ADD A, L
23 | LD L, A
24 | ADC A, H
25 | SUB L
26 | LD H, A
27 | ENDMACRO
28 |
29 | ; Set a GPIO register
30 | ; Parameters:
31 | ; - REG: Register to set
32 | ; - VAL: Bit(s) to set (1: set, 0: ignore)
33 | ;
34 | SET_GPIO: MACRO REG, VAL
35 | IN0 A,(REG)
36 | OR VAL
37 | OUT0 (REG),A
38 | ENDMACRO
39 |
40 | ; Reset a GPIO register
41 | ; Parameters:
42 | ; - REG: Register to set
43 | ; - VAL: Bit(s) to reset (1: reset, 0: ignore)
44 | ;
45 | RES_GPIO: MACRO REG, VAL
46 | PUSH BC
47 | LD A, VAL
48 | CPL
49 | LD C, A
50 | IN0 A,(REG)
51 | AND C
52 | OUT0 (REG),A
53 | POP BC
54 | ENDMACRO
55 |
56 | ; Get a GPIO register
57 | ; Parameters:
58 | ; - REG: Register to test
59 | ; - VAL: Bit(s) to test
60 | ;
61 | GET_GPIO: MACRO REG, VAL
62 | IN0 A,(REG)
63 | TST A, VAL
64 | ENDMACRO
65 |
66 | ; Set a TIMER to the value in register BC
67 | ; Parameters:
68 | ; - TIMER: Timer to set
69 | ;
70 | TIMER_SET_BC: MACRO TIMER
71 | IF TIMER >= 0 && TIMER <= 5
72 | LD A,%0C
73 | OUT0 (TMR&TIMER&_CTL),A
74 | OUT0 (TMR&TIMER&_RR_L),C
75 | OUT0 (TMR&TIMER&_RR_H),B
76 | ELSE
77 | ERROR
78 | ENDIF
79 | ENDMACRO
80 |
81 | ; Set a timer to the given *constant* value in milliseconds
82 | ; Parameters:
83 | ; - TIMER: Timer set set
84 | ; - DELAY_MS: Constant delay in milliseconds
85 | ;
86 | TIMER_SET: MACRO TIMER,DELAY_MS
87 | IF DELAY_MS < 1 || DELAY_MS > 910
88 | ERROR
89 | ENDIF
90 | LD BC,MASTERCLOCK / 1000 / 256 * DELAY_MS
91 | TIMER_SET_BC 0
92 | ENDMACRO
93 |
94 | ;Start a timer
95 | ; Parameters:
96 | ; - TIMER: Timer to start
97 | ;
98 | TIMER_START: MACRO TIMER
99 | IF TIMER >= 0 && TIMER <= 5
100 | IN0 A,(TMR&TIMER&_CTL)
101 | OR A,%03
102 | OUT0 (TMR&TIMER&_CTL),A
103 | ENDIF
104 | ENDMACRO
105 |
106 | ; Reset a timer to stop it running
107 | ; Leaves the rest of the timer configuration unchanged so it can be restarted
108 | ; Parameters:
109 | ; - TIMER: Timer to reset
110 | ;
111 | TIMER_RESET: MACRO TIMER
112 | IF TIMER >= 0 && TIMER <= 5
113 | IN0 A,(TMR&TIMER&_CTL)
114 | AND A,11111100b
115 | OUT0 (TMR&TIMER&_CTL),A
116 | ELSE
117 | ERROR
118 | ENDIF
119 | ENDMACRO
120 |
121 | ; Test if the given timer has expired
122 | ; Parameters:
123 | ; - TIMER: Timer to test
124 | ; Output:
125 | ; - AF: Carry Flag set or clear depending on whether timer has expired
126 | ;
127 | TIMER_EXP?: MACRO TIMER
128 | IF TIMER >= 0 && TIMER <= 5
129 | IN0 A,(TMR&TIMER&_CTL)
130 | ELSE
131 | ERROR
132 | ENDIF
133 | RLA
134 | ENDMACRO
135 |
136 | ; Wait for a timer to count down to zero
137 | ; Parameters:
138 | ; - TIMER: Timer to wait on
139 | ;
140 | TIMER_WAIT: MACRO TIMER
141 | $$loop: TIMER_EXP? TIMER
142 | JR NC,$$loop
143 | ENDMACRO
144 |
145 | ; Delay for the given (constant) number of milliseconds using *Timer 0*
146 | ; Parameters:
147 | ; - DELAY_MS: Number of milliseconds to delay
148 | ;
149 | ; NB: Can wait between 1 and 910 milliseconds based on 18.432MHz clock
150 | ;
151 | DELAY_MS: MACRO DELAY_MS
152 | IF DELAY_MS < 1 || DELAY_MS > 910
153 | ERROR
154 | ENDIF
155 | LD BC,MASTERCLOCK / 1000 / 256 * DELAY_MS
156 | PUSH BC
157 | CALL _timer0_delay
158 | POP BC
159 | ENDMACRO
160 |
161 | END
162 |
--------------------------------------------------------------------------------
/src/misc.asm:
--------------------------------------------------------------------------------
1 | ;
2 | ; Title: AGON MOS - Miscellaneous helper functions
3 | ; Author: Dean Belfield
4 | ; Created: 24/07/2022
5 | ; Last Updated: 15/04/2023
6 | ;
7 | ; Modinfo:
8 | ; 03/08/2022: Added SET_AHL24 and SET_ADE24
9 | ; 10/08/2022: Optimised SET_ADE24
10 | ; 08/11/2022: Fixed return value bug in exec16
11 | ; 19/11/2022: Added exec24 and params for exec16/24 functions
12 | ; 09/03/2023: Added wait_timer0
13 | ; 20/03/2023: Function exec24 now preserves MB
14 | ; 15/04/2023: Added GET_AHL24
15 |
16 | INCLUDE "macros.inc"
17 | INCLUDE "equs.inc"
18 |
19 | .ASSUME ADL = 1
20 |
21 | DEFINE .STARTUP, SPACE = ROM
22 | SEGMENT .STARTUP
23 |
24 | XDEF SWITCH_A
25 | XDEF SET_AHL24
26 | XDEF GET_AHL24
27 | XDEF SET_ADE24
28 |
29 | XDEF __exec16
30 | XDEF __exec24
31 | XDEF __wait_timer0
32 |
33 | XDEF _exec16
34 | XDEF _exec24
35 | XDEF _wait_timer0
36 | XDEF _timer0_delay
37 |
38 | XREF _callSM
39 |
40 | ; Switch on A - lookup table immediately after call
41 | ; A: Index into lookup table
42 | ;
43 | SWITCH_A: EX (SP), HL ; Swap HL with the contents of the top of the stack
44 | ADD A, A ; Multiply A by two
45 | ADD8U_HL ; Add to HL (macro)
46 | LD A, (HL) ; follow the call. Fetch an address from the
47 | INC HL ; table.
48 | LD H, (HL)
49 | LD L, A
50 | EX (SP), HL ; Swap this new address back, restores HL
51 | RET ; Return program control to this new address
52 |
53 | ; Set the MSB of HL (U) to A
54 | ;
55 | SET_AHL24: PUSH HL
56 | LD HL, 2
57 | ADD HL, SP
58 | LD (HL), A
59 | POP HL
60 | RET
61 |
62 | ; Get the MSB of HL (U) in A
63 | ;
64 | GET_AHL24: PUSH HL
65 | LD HL, 2
66 | ADD HL, SP
67 | LD A, (HL)
68 | POP HL
69 | RET
70 |
71 | ; Set the MSB of DE (U) to A
72 | ;
73 | SET_ADE24: EX DE, HL
74 | PUSH HL
75 | LD HL, 2
76 | ADD HL, SP
77 | LD (HL), A
78 | POP HL
79 | EX DE, HL
80 | RET
81 |
82 | ; Execute a program in RAM
83 | ; int * _exec24(UINT24 address, char * params)
84 | ; Params:
85 | ; - address: The 24-bit address to call
86 | ; - params: 24-bit pointer to the params buffer
87 | ;
88 | ; This function will call the 24 bit address and keep the eZ80 in ADL mode
89 | ; The called function must do a RET and take care of preserving registers
90 | ;
91 | __exec24:
92 | _exec24: PUSH IY
93 | LD IY, 0
94 | ADD IY, SP ; Standard prologue
95 | PUSH AF ; Stack any registers being used
96 | PUSH DE
97 | PUSH IX
98 | LD A, MB ; Preserve the MBASE register
99 | PUSH AF
100 | LD DE, (IY+6) ; Get the address
101 | LD A, (IY+8) ; And the high byte for the code segment
102 | LD HL, (IY+9) ; Load HLU with the pointer to the params
103 | ;
104 | ; Write out a short subroutine "JP (DE)" to RAM
105 | ;
106 | LD IX, _callSM ; Storage for the self modified routine
107 | LD (IX + 0), C3h ; JP llhhuu
108 | LD (IX + 1), E
109 | LD (IX + 2), D
110 | LD (IX + 3), A
111 | ;
112 | JR _execSM ; Save some bytes, jump to _exec16 to finish the call
113 |
114 | ; Execute a program in RAM
115 | ; int * _exec16(UINT24 address, char * params)
116 | ; Params:
117 | ; - address: The 24-bit address to call
118 | ; - params: 24-bit pointer to the params buffer
119 | ;
120 | ; This function will call the 24 bit address and switch the eZ80 into Z80 mode (ADL=0)
121 | ; The called function must do a RET.LIS (49h, C9h) and take care of preserving registers
122 | ;
123 | __exec16:
124 | _exec16: PUSH IY
125 | LD IY, 0
126 | ADD IY, SP ; Standard prologue
127 | PUSH AF ; Stack any registers being used
128 | PUSH DE
129 | PUSH IX
130 | LD A, MB ; Preserve the MBASE register
131 | PUSH AF
132 | LD DE, (IY+6) ; Get the address
133 | LD A, (IY+8) ; And the high byte for the code segment
134 | LD MB, A ; Set the MBASE register
135 | LD HL, (IY+9) ; Load HLU with the pointer to the params
136 | ;
137 | ; Write out a short subroutine "CALL.IS (DE): RET" to RAM
138 | ;
139 |
140 | LD IX, _callSM ; Storage for the self modified routine
141 | LD (IX + 0), 49h ; CALL.IS llhh
142 | LD (IX + 1), CDh
143 | LD (IX + 2), E
144 | LD (IX + 3), D
145 | LD (IX + 4), C9h ; RET
146 | ;
147 | _execSM: CALL _callSM ; Call the subroutine
148 | ;
149 | POP AF ; Restore the MBASE register
150 | LD MB, A
151 | POP IX
152 | POP DE
153 | POP AF
154 | LD SP, IY ; Standard epilogue
155 | POP IY
156 | RET
157 |
158 | ; Wait for timer0 to hit 0
159 | ;
160 | __wait_timer0:
161 | _wait_timer0: PUSH AF
162 | PUSH BC
163 | IN0 A, (TMR0_CTL) ; Enable the timer
164 | OR 3
165 | OUT0 (TMR0_CTL), A
166 | $$: IN0 B, (TMR0_DR_L) ; Fetch the counter L
167 | IN0 A, (TMR0_DR_H) ; And the counter H
168 | OR B
169 | JR NZ, $B
170 | POP BC
171 | POP AF
172 | RET
173 |
174 | _timer0_delay:
175 | POP HL
176 | POP BC
177 | PUSH BC
178 | TIMER_SET_BC 0
179 | TIMER_START 0
180 | TIMER_WAIT 0
181 | JP (HL)
182 |
183 | END
184 |
--------------------------------------------------------------------------------
/src/mos.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Title: AGON MOS - MOS code
3 | * Author: Dean Belfield
4 | * Created: 10/07/2022
5 | * Last Updated: 11/11/2023
6 | *
7 | * Modinfo:
8 | * 11/07/2022: Removed mos_cmdBYE, Added mos_cmdLOAD
9 | * 12/07/2022: Added mos_cmdJMP
10 | * 13/07/2022: Added mos_cmdSAVE
11 | * 14/07/2022: Added mos_cmdRUN
12 | * 24/07/2022: Added mos_getkey
13 | * 03/08/2022: Added a handful of MOS API calls
14 | * 05/08/2022: Added mos_FEOF
15 | * 05/09/2022: Added mos_cmdREN, mos_cmdBOOT; moved mos_EDITLINE into mos_editline.c
16 | * 25/09/2022: Added mos_GETERROR, mos_MKDIR
17 | * 13/10/2022: Added mos_OSCLI and supporting code
18 | * 20/10/2022: Tweaked error handling
19 | * 13/11/2022: Added mos_cmp
20 | * 21/11/2022: Added support for passing params to executables & ADL mode
21 | * 14/02/2023: Added mos_cmdVDU
22 | * 20/02/2023: Function mos_getkey now returns a BYTE
23 | * 09/03/2023: Added mos_cmdTIME, mos_cmdCREDITS, mos_DIR now accepts a path
24 | * 14/03/2023: Added mos_cmdCOPY and mos_COPY
25 | * 15/03/2023: Added mos_GETRTC, mos_SETRTC
26 | * 21/03/2023: Added mos_SETINTVECTOR
27 | * 14/04/2023: Added fat_EOF
28 | * 15/04/2023: Added mos_GETFIL, mos_FREAD, mos_FWRITE, mos_FLSEEK
29 | * 30/05/2023: Function mos_FGETC now returns EOF flag
30 | * 08/07/2023 Added mos_trim function
31 | * 11/11/2023: Added mos_cmdHELP, mos_cmdTYPE, mos_cmdCLS, mos_cmdMOUNT
32 | */
33 |
34 | #ifndef MOS_H
35 | #define MOS_H
36 |
37 | #include "ff.h"
38 |
39 | typedef struct {
40 | char * name;
41 | int (*func)(char * ptr);
42 | char * args;
43 | char * help;
44 | char * aliases;
45 | } t_mosCommand;
46 |
47 | typedef struct {
48 | UINT8 free;
49 | FIL fileObject;
50 | } t_mosFileObject;
51 |
52 | void mos_error(int error);
53 |
54 | BYTE mos_getkey(void);
55 | UINT24 mos_input(char * buffer, int bufferLength);
56 | void * mos_getCommand(char * ptr);
57 | BOOL mos_cmp(char *p1, char *p2);
58 | char * mos_trim(char * s);
59 | char * mos_strtok(char *s1, char * s2);
60 | char * mos_strtok_r(char *s1, const char *s2, char **ptr);
61 | int mos_exec(char * buffer);
62 | UINT8 mos_execMode(UINT8 * ptr);
63 |
64 | int mos_mount(void);
65 |
66 | BOOL mos_parseNumber(char * ptr, UINT24 * p_Value);
67 | BOOL mos_parseString(char * ptr, char ** p_Value);
68 |
69 | int mos_cmdDIR(char * ptr);
70 | int mos_cmdLOAD(char * ptr);
71 | int mos_cmdSAVE(char *ptr);
72 | int mos_cmdDEL(char * ptr);
73 | int mos_cmdJMP(char * ptr);
74 | int mos_cmdRUN(char * ptr);
75 | int mos_cmdCD(char * ptr);
76 | int mos_cmdREN(char *ptr);
77 | int mos_cmdCOPY(char *ptr);
78 | int mos_cmdMKDIR(char *ptr);
79 | int mos_cmdSET(char *ptr);
80 | int mos_cmdVDU(char *ptr);
81 | int mos_cmdTIME(char *ptr);
82 | int mos_cmdCREDITS(char *ptr);
83 | int mos_cmdTYPE(char *ptr);
84 | int mos_cmdCLS(char *ptr);
85 | int mos_cmdMOUNT(char *ptr);
86 | int mos_cmdHELP(char *ptr);
87 |
88 | UINT24 mos_LOAD(char * filename, UINT24 address, UINT24 size);
89 | UINT24 mos_SAVE(char * filename, UINT24 address, UINT24 size);
90 | UINT24 mos_TYPE(char * filename);
91 | UINT24 mos_CD(char * path);
92 | UINT24 mos_DIR(char * path);
93 | UINT24 mos_DEL(char * filename);
94 | UINT24 mos_REN(char * filename1, char * filename2);
95 | UINT24 mos_COPY(char * filename1, char * filename2);
96 | UINT24 mos_MKDIR(char * filename);
97 | UINT24 mos_BOOT(char * filename, char * buffer, UINT24 size);
98 |
99 | UINT24 mos_FOPEN(char * filename, UINT8 mode);
100 | UINT24 mos_FCLOSE(UINT8 fh);
101 | UINT24 mos_FGETC(UINT8 fh);
102 | void mos_FPUTC(UINT8 fh, char c);
103 | UINT24 mos_FREAD(UINT8 fh, UINT24 buffer, UINT24 btr);
104 | UINT24 mos_FWRITE(UINT8 fh, UINT24 buffer, UINT24 btw);
105 | UINT8 mos_FLSEEK(UINT8 fh, UINT32 offset);
106 | UINT8 mos_FEOF(UINT8 fh);
107 |
108 | void mos_GETERROR(UINT8 errno, UINT24 address, UINT24 size);
109 | UINT24 mos_OSCLI(char * cmd);
110 | UINT8 mos_GETRTC(UINT24 address);
111 | void mos_SETRTC(UINT24 address);
112 | UINT24 mos_SETINTVECTOR(UINT8 vector, UINT24 address);
113 | UINT24 mos_GETFIL(UINT8 fh);
114 |
115 | UINT8 fat_EOF(FIL * fp);
116 |
117 | #define HELP_CAT "Directory listing of the current directory\r\n"
118 | #define HELP_CAT_ARGS ""
119 | #define HELP_CAT_ALIASES "DIR and ."
120 | #define HELP_DIR_ALIASES "CAT and ."
121 | #define HELP_DOT_ALIASES "CAT and DIR"
122 |
123 | #define HELP_CD "Change current directory\r\n"
124 | #define HELP_CD_ARGS ""
125 |
126 | #define HELP_COPY "Create a copy of a file\r\n"
127 | #define HELP_COPY_ARGS " "
128 |
129 | #define HELP_CREDITS "Output credits and version numbers for\r\n" \
130 | "third-party libraries used in the Agon firmware\r\n"
131 |
132 | #define HELP_DELETE "Delete a file or folder (must be empty)\r\n"
133 | #define HELP_DELETE_ARGS ""
134 | #define HELP_DELETE_ALIASES "ERASE"
135 | #define HELP_ERASE_ALIASES "DELETE"
136 |
137 | #define HELP_JMP "Jump to the specified address in memory\r\n"
138 | #define HELP_JMP_ARGS ""
139 |
140 | #define HELP_LOAD "Load a file from the SD card to the specified address.\r\n" \
141 | "If no `addr` parameter is passed it will" \
142 | "default to &40000\r\n"
143 | #define HELP_LOAD_ARGS " []"
144 |
145 | #define HELP_MKDIR "Create a new folder on the SD card\r\n"
146 | #define HELP_MKDIR_ARGS ""
147 |
148 | #define HELP_RENAME "Rename a file in the same folder\r\n"
149 | #define HELP_RENAME_ARGS " "
150 | #define HELP_RENAME_ALIASES "MOVE"
151 | #define HELP_MOVE_ALIASES "RENAME"
152 |
153 | #define HELP_RUN "Call an executable binary loaded in memory.\r\n" \
154 | "If no parameters are passed, then addr will " \
155 | "default to &40000.\r\n"
156 | #define HELP_RUN_ARGS "[]"
157 |
158 | #define HELP_SAVE "Save a block of memory to the SD card\r\n"
159 | #define HELP_SAVE_ARGS " "
160 |
161 | #define HELP_SET "Set a system option\r\n\r\n" \
162 | "Keyboard Layout\r\n" \
163 | "SET KEYBOARD n: Set the keyboard layout\r\n" \
164 | " 0: UK (default)\r\n" \
165 | " 1: US\r\n" \
166 | " 2: German\r\n" \
167 | " 3: Italian\r\n" \
168 | " 4: Spanish\r\n" \
169 | " 5: French\r\n" \
170 | " 6: Belgian\r\n" \
171 | " 7: Norwegian\r\n" \
172 | " 8: Japanese\r\n" \
173 | " 9: US International\r\n" \
174 | " 10: US International (alternative)\r\n" \
175 | " 11: Swiss (German)\r\n" \
176 | " 12: Swiss (French)\r\n" \
177 | " 13: Danish\r\n" \
178 | " 14: Swedish\r\n" \
179 | " 15: Portuguese\r\n\r\n" \
180 | "Serial Console\r\n" \
181 | "SET CONSOLE n: Serial console\r\n" \
182 | " 0: Console off (default)\r\n" \
183 | " 1: Console on\r\n"
184 | #define HELP_SET_ARGS "