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