Sat, 19 Apr 2014 02:19:39 -0600
fixed timing on OSes that set a minimum time for sleeps (previously the main loop code assumed no minimum sleep time; the new version uses longer sleeps less frequently)
1 EXAMPLE:
2 -------
3 As an example, I'll build an imaginary hardware platform.
6 The system is fairly simple, comprising of a 000, an input device, an output
7 device, a non-maskable-interrupt device, and an interrupt controller.
10 The input device receives input from the user and asserts its interrupt
11 request line until its value is read. Reading from the input device's
12 memory-mapped port will both clear its interrupt request and read an ASCII
13 representation (8 bits) of what the user entered.
15 The output device reads value when it is selected through its memory-mapped
16 port and outputs it to a display. The value it reads will be interpreted as
17 an ASCII value and output to the display. The output device is fairly slow
18 (it can only process 1 byte per second), and so it asserts its interrupt
19 request line when it is ready to receive a byte. Writing to the output device
20 sends a byte to it. If the output device is not ready, the write is ignored.
21 Reading from the output device returns 0 and clears its interrupt request line
22 until another byte is written to it and 1 second elapses.
24 The non-maskable-interrupt (NMI) device, as can be surmised from the name,
25 generates a non-maskable-interrupt. This is connected to some kind of external
26 switch that the user can push to generate a NMI.
28 Since there are 3 devices interrupting the CPU, an interrupt controller is
29 needed. The interrupt controller takes 7 inputs and encodes the highest
30 priority asserted line on the 3 output pins. the input device is wired to IN2
31 and the output device is wired to IN1 on the controller. The NMI device is
32 wired to IN7 and all the other inputs are wired low.
34 The bus is also connected to a 1K ROM and a 256 byte RAM.
35 Beware: This platform places ROM and RAM in the same address range and uses
36 the FC pins to select the correct address space!
37 (You didn't expect me to make it easy, did you? =)
39 There are two ways to handle address spaces with Musashi:
41 1. Enable M68K_SEPARATE_READS and make handler functions for immediate and
42 pc-relative reads.
44 2. Enable M68K_EMULATE_FC and make a callback function for function code
45 changes.
47 Both methods will work in this case, but I've opted for the "more correct"
48 function code pin emulation for this example.
52 Here is the schematic in all its ASCII splendour:
53 -------------------------------------------------
55 NMI TIED
56 SWITCH LOW
57 | |
58 | +-+-+-+
59 | | | | | +------------------------------------------------+
60 | | | | | | +------------------------------------+ |
61 | | | | | | | | |
62 +-------------+ | |
63 |7 6 5 4 3 2 1| | |
64 | | | |
65 | INT CONTRLR | | |
66 | | | |
67 |i i i | | |
68 |2 1 0 | | |
69 +-------------+ | |
70 | | | | |
71 | | | +--------------------------------+--+ | |
72 o o o | | | | |
73 +--------------+ +-------+ +----------+ +---------+ +----------+
74 | I I I a | | | | | | r a i | | i |
75 | 2 1 0 23 | | | | | | e c | | |
76 | | | | | | | a k | | |
77 | | | | | | | d | | |
78 | | | | | | | | | |
79 | M68000 | | ROM | | RAM | | IN | | OUT |
80 | | | | | | | | | |
81 | a9|--|a9 |--| |--| |--| |
82 | a8|--|a8 |--| |--| |--| |
83 | a7|--|a7 |--|a7 |--| |--| |
84 | a6|--|a6 |--|a6 |--| |--| |
85 | a5|--|a5 |--|a5 |--| |--| |
86 | a4|--|a4 |--|a4 |--| |--| |
87 | a3|--|a3 |--|a3 |--| |--| |
88 | a2|--|a2 |--|a2 |--| |--| |
89 | a1|--|a1 |--|a1 |--| |--| |
90 | a0|--|a0 |--|a0 |--| |--| |
91 | | | | | | | | | |
92 | d15|--|d15 |--|d15 |--| |--| |
93 | d14|--|d14 |--|d14 |--| |--| |
94 | d13|--|d13 |--|d13 |--| |--| |
95 | d12|--|d12 |--|d12 |--| |--| |
96 | d11|--|d11 |--|d11 |--| |--| |
97 | d10|--|d10 |--|d10 |--| |--| |
98 | d9|--|d9 |--|d9 |--| |--| |
99 | d8|--|d8 |--|d8 |--| |--| |
100 | d7|--|d7 |--|d7 |--|d7 |--|d7 |
101 | d6|--|d6 |--|d6 |--|d6 |--|d6 |
102 | d5|--|d5 |--|d5 |--|d5 |--|d5 |
103 | d4|--|d4 |--|d4 |--|d4 |--|d4 |
104 | d3|--|d3 |--|d3 |--|d3 |--|d3 |
105 | d2|--|d2 |--|d2 |--|d2 |--|d2 |
106 | d1|--|d1 |--|d1 |--|d1 |--|d1 w |
107 | d0|--|d0 |--|d0 |--|d0 |--|d0 r |
108 | | | | | | | | | i a |
109 | a F F F | | | | | | | | t c |
110 |22 rW 2 1 0 | | cs | | cs rW | | | | e k |
111 +--------------+ +-------+ +----------+ +---------+ +----------+
112 | | | | | | | | | |
113 | | | | | | | | | |
114 | | | | | +-------+ +-----+ | +---+ |
115 | | | | | | IC1 | | IC2 | | |AND| |
116 | | | | | |a b c d| |a b c| | +---+ |
117 | | | | | +-------+ +-----+ | | | |
118 | | | | | | | | | | | | | | +--+
119 | | | | | | | | | | | | | | |
120 | | | | | | | | | | | | | | |
121 | | | | | | | | | | | | | | |
122 | | | | +-----)-)-+-)----)-)-+ | | |
123 | | | +-------)-+---)----)-+ | | |
124 | | +---------+-----)----+ | | |
125 | | | | | |
126 | +------------------+-----------+----------------------+ |
127 | |
128 +-----------------------------------------------------------+
130 IC1: output=1 if a=0 and b=1 and c=0 and d=0
131 IC2: output=1 if a=0 and b=0 and c=1
135 Here is the listing for program.bin:
136 -----------------------------------
138 INPUT_ADDRESS equ $800000
139 OUTPUT_ADDRESS equ $400000
140 CIRCULAR_BUFFER equ $c0
141 CAN_OUTPUT equ $d0
142 STACK_AREA equ $100
144 vector_table:
145 00000000 0000 0100 dc.l STACK_AREA * 0: SP
146 00000004 0000 00c0 dc.l init * 1: PC
147 00000008 0000 0148 dc.l unhandled_exception * 2: bus error
148 0000000c 0000 0148 dc.l unhandled_exception * 3: address error
149 00000010 0000 0148 dc.l unhandled_exception * 4: illegal instruction
150 00000014 0000 0148 dc.l unhandled_exception * 5: zero divide
151 00000018 0000 0148 dc.l unhandled_exception * 6: chk
152 0000001c 0000 0148 dc.l unhandled_exception * 7: trapv
153 00000020 0000 0148 dc.l unhandled_exception * 8: privilege violation
154 00000024 0000 0148 dc.l unhandled_exception * 9: trace
155 00000028 0000 0148 dc.l unhandled_exception * 10: 1010
156 0000002c 0000 0148 dc.l unhandled_exception * 11: 1111
157 00000030 0000 0148 dc.l unhandled_exception * 12: -
158 00000034 0000 0148 dc.l unhandled_exception * 13: -
159 00000038 0000 0148 dc.l unhandled_exception * 14: -
160 0000003c 0000 0148 dc.l unhandled_exception * 15: uninitialized interrupt
161 00000040 0000 0148 dc.l unhandled_exception * 16: -
162 00000044 0000 0148 dc.l unhandled_exception * 17: -
163 00000048 0000 0148 dc.l unhandled_exception * 18: -
164 0000004c 0000 0148 dc.l unhandled_exception * 19: -
165 00000050 0000 0148 dc.l unhandled_exception * 20: -
166 00000054 0000 0148 dc.l unhandled_exception * 21: -
167 00000058 0000 0148 dc.l unhandled_exception * 22: -
168 0000005c 0000 0148 dc.l unhandled_exception * 23: -
169 00000060 0000 0148 dc.l unhandled_exception * 24: spurious interrupt
170 00000064 0000 0136 dc.l output_ready * 25: l1 irq
171 00000068 0000 010e dc.l input_ready * 26: l2 irq
172 0000006c 0000 0148 dc.l unhandled_exception * 27: l3 irq
173 00000070 0000 0148 dc.l unhandled_exception * 28: l4 irq
174 00000074 0000 0148 dc.l unhandled_exception * 29: l5 irq
175 00000078 0000 0148 dc.l unhandled_exception * 30: l6 irq
176 0000007c 0000 014e dc.l nmi * 31: l7 irq
177 00000080 0000 0148 dc.l unhandled_exception * 32: trap 0
178 00000084 0000 0148 dc.l unhandled_exception * 33: trap 1
179 00000088 0000 0148 dc.l unhandled_exception * 34: trap 2
180 0000008c 0000 0148 dc.l unhandled_exception * 35: trap 3
181 00000090 0000 0148 dc.l unhandled_exception * 36: trap 4
182 00000094 0000 0148 dc.l unhandled_exception * 37: trap 5
183 00000098 0000 0148 dc.l unhandled_exception * 38: trap 6
184 0000009c 0000 0148 dc.l unhandled_exception * 39: trap 7
185 000000a0 0000 0148 dc.l unhandled_exception * 40: trap 8
186 000000a4 0000 0148 dc.l unhandled_exception * 41: trap 9
187 000000a8 0000 0148 dc.l unhandled_exception * 42: trap 10
188 000000ac 0000 0148 dc.l unhandled_exception * 43: trap 11
189 000000b0 0000 0148 dc.l unhandled_exception * 44: trap 12
190 000000b4 0000 0148 dc.l unhandled_exception * 45: trap 13
191 000000b8 0000 0148 dc.l unhandled_exception * 46: trap 14
192 000000bc 0000 0148 dc.l unhandled_exception * 47: trap 15
193 * This is the end of the useful part of the table.
194 * We will now do the Capcom thing and put code starting at $c0.
196 init:
197 * Copy the exception vector table to RAM.
198 000000c0 227c 0000 0000 move.l #0, a1 * a1 is RAM index
199 000000c6 303c 002f move.w #47, d0 * d0 is counter (48 vectors)
200 000000ca 41fa 0006 lea.l (copy_table,PC), a0 * a0 is scratch
201 000000ce 2208 move.l a0, d1 * d1 is ROM index
202 000000d0 4481 neg.l d1
203 copy_table:
204 000000d2 22fb 18fe dc.l $22fb18fe * #%#$ as68k generates 020 code here
205 * move.l (copy_table,PC,d1.l), (a1)+
206 000000d6 5841 addq #4, d1
207 000000d8 51c8 fff8 dbf d0, copy_table
209 main_init:
210 * Initialize main program
211 000000dc 11fc 0000 00d0 move.b #0, CAN_OUTPUT
212 000000e2 4df8 00c0 lea.l CIRCULAR_BUFFER, a6
213 000000e6 7c00 moveq #0, d6 * output buffer ptr
214 000000e8 7e00 moveq #0, d7 * input buffer ptr
215 000000ea 027c f8ff andi #$f8ff, SR * clear interrupt mask
216 main:
217 * Main program
218 000000ee 4a38 00d0 tst.b CAN_OUTPUT * can we output?
219 000000f2 67fa beq main
220 000000f4 be06 cmp.b d6, d7 * is there data?
221 000000f6 67f6 beq main
222 000000f8 11fc 0000 00d0 move.b #0, CAN_OUTPUT
223 000000fe 13f6 6000 0040 move.b (0,a6,d6.w), OUTPUT_ADDRESS * write data
224 0000
225 00000106 5246 addq #1, d6
226 00000108 0206 000f andi.b #15, d6 * update circular buffer
227 0000010c 60e0 bra main
230 input_ready:
231 0000010e 2f00 move.l d0, -(a7)
232 00000110 2f01 move.l d1, -(a7)
233 00000112 1239 0080 0000 move.b INPUT_ADDRESS, d1 * read data
234 00000118 1007 move.b d7, d0 * check if buffer full
235 0000011a 5240 addq #1, d0
236 0000011c 0200 000f andi.b #15, d0
237 00000120 bc00 cmp.b d0, d6
238 00000122 6700 000c beq input_ready_quit * throw away if full
239 00000126 1d81 7000 move.b d1, (0,a6,d7.w) * store the data
240 0000012a 5247 addq #1, d7
241 0000012c 0207 000f andi.b #15, d7 * update circular buffer
242 input_ready_quit:
243 00000130 221f move.l (a7)+, d1
244 00000132 201f move.l (a7)+, d0
245 00000134 4e73 rte
247 output_ready:
248 00000136 2f00 move.l d0, -(a7)
249 00000138 11fc 0001 00d0 move.b #1, CAN_OUTPUT
250 0000013e 1039 0040 0000 move.b OUTPUT_ADDRESS, d0 * acknowledge the interrupt
251 00000144 201f move.l (a7)+, d0
252 00000146 4e73 rte
254 unhandled_exception:
255 00000148 4e72 2700 stop #$2700 * wait for NMI
256 0000014c 60fa bra unhandled_exception * shouldn't get here
258 nmi:
259 * perform a soft reset
260 0000014e 46fc 2700 move #$2700, SR * set status register
261 00000152 2e7a feac move.l (vector_table,PC), a7 * reset stack pointer
262 00000156 4e70 reset * reset peripherals
263 00000158 4efa feaa jmp (vector_table+4,PC) * reset program counter
265 END
269 Compiling the example host environment:
270 --------------------------------------
272 The following assumes that you are using a GNU-based compiler such as gcc or
273 djgpp for DOS (available free from www.delorie.com).
274 If you are using a commercial compiler, you may have to modify the makefile
275 or generate your own project file.
276 Also note that part of the compilation process involves the compilation and
277 invokation of the m68kmake program.
279 - Copy the m68k files to a directory. Then copy the host environment files to
280 the same directory, overwriting m68kconf.h. program.bin is the actual 68000
281 program you will be running.
282 - Modify osd_get_key() in sim.c to suit your environment (currently set for
283 the free djgpp compiler, available from www.delorie.com, under DOS).
284 - Type make
285 - Perform the necessary animal sacrifices.
286 - Type sim program.bin
289 Keys:
290 ESC - quits the simulator
291 ~ - generates an NMI interrupt
292 Any other key - Genearate input for the input device
295 Note: I've cheated a bit in the emulation. There is no speed control
296 to set the speed the CPU runs at; it simply runs as fast as your
297 processor can run it.
298 To add speed control, you will need a high-precision timestamp
299 function (like the RDTSC instruction for newer Pentium CPUs)
300 and a bit of arithmetic to make the cycles argument for m68k_execute().
301 I'll leave that as an excercise to the reader.