F00:0001 ; ==================================================================================== F00:0002 ; MPF_UTILS_RAC.Z80 - Simple utilities for Multitech Micro-professor MPF-1B F00:0003 ; ==================================================================================== F00:0004 ; F00:0005 ; This utility ROM image is designed to be burnt onto a 2716, 2732 or 2764/27C64 F00:0006 ; device and installed in the expansion socket U7 on the motherboard. F00:0007 ; F00:0008 ; All routines intended for external invovation are preceeded by the EXEC_ keyword. F00:0009 ; This should show up clearly at the ened of the generated assembly listing file. F00:0010 ; F00:0011 ; ==================================================================================== F00:0012 ; Modification record: F00:0013 ; 20/10/15 RAC v1.0 Initial Version F00:0014 ; 26/10/17 RAC v1.1 Corrected problem with countdown timer decrement F00:0015 ; F00:0016 ; ==================================================================================== F00:0017 F00:0018 ; ==================================================================================== F00:0019 ; Definitions for built in monitor routines/data (MPF-1A or MPF-1B) F00:0020 F00:0021 HEX7SG EQU $0678 ; output data to 7 segment displays F00:0022 SCAN EQU $05FE ; wait until keypress detected F00:0023 SCAN1 EQU $0624 ; keyboard and display cyclic scan F00:0024 TONE EQU $05E4 ; generate speaker tone F00:0025 BLANK EQU $07A5 ; blank display data F00:0026 RAMCHK EQU $05F6 ; checks RAM memory location F00:0027 TONE1K EQU $05DE ; generate 1KHz tone for defined period F00:0028 F00:0029 ; ==================================================================================== F00:0030 ; Definitions for built in Z80 and peripheral registers F00:0031 F00:0032 CTC0 EQU $40 ; CTC 0 register F00:0033 DIGIT EQU $02 ; Used for speaker output F00:0034 KIN EQU $00 ; Used for raw keyboard input F00:0035 F00:0036 ; ==================================================================================== F00:0037 ; Definitions for ROM addressing and size F00:0038 F00:0039 MON_ROM_BASE_START EQU $0000 F00:0040 MON_ROM_BASE_SIZE EQU $0800 F00:0041 MON_ROM_TOP_START EQU $0800 F00:0042 MON_ROM_TOP_SIZE EQU $0800 F00:0043 MON_ROM_FULL_SIZE EQU (MON_ROM_BASE_SIZE + MON_ROM_TOP_SIZE) F00:0044 F00:0045 EXP_ROM_START EQU $2000 F00:0046 EXP_ROM_BASE_SIZE EQU $0800 F00:0047 EXP_ROM_JUMP_TAB EQU $2080 F00:0048 EXP_ROM_CODE_START EQU $2100 F00:0049 F00:0050 ; These definitions are for accessing the interrupt vectors from the code F00:0051 ; These MUST be aligned to a 3 bit boundary (last 3 bits are zero) F00:0052 EXP_ROM_INT_VECT1 EQU $20E0 F00:0053 EXP_ROM_INT_VECT1_BASE EQU (EXP_ROM_INT_VECT1 & $FF) F00:0054 EXP_ROM_INT_VECT2 EQU $20F0 F00:0055 EXP_ROM_INT_VECT2_BASE EQU (EXP_ROM_INT_VECT2 & $FF) F00:0056 F00:0057 ; Expansion ROM variable area in RAM (avoid aliassing onto address test boundary) F00:0058 EXP_ROM_RAM_VAR_START EQU $1910 F00:0059 F00:0060 ; Monitor ISR locations for overriding with User ISR F00:0061 MON_ISR_SVC_ADDR EQU $1FFE F00:0062 F00:0063 ; ==================================================================================== F00:0064 ; Definition for RAM memory (fixed at 2K starting at $1800) F00:0065 F00:0066 RAM_MEM_START EQU $1800 F00:0067 RAM_MEM_SIZE EQU $0800 F00:0068 F00:0069 ; ==================================================================================== F00:0070 ; Misc definitions for this module F00:0071 F00:0072 DispBuffDataLen EQU 2 F00:0073 DispBuffAddrLen EQU 4 F00:0074 DispBuffFullLen EQU (DispBuffDataLen + DispBuffAddrLen) F00:0075 F00:0076 ; Delay specifications for this system F00:0077 MAX_BASE_DELAY_VAL EQU $FFFF F00:0078 SCAN_BASE_DELAY_VAL EQU $0020 F00:0079 FAIL_MSG_HL_DELAY EQU 3 F00:0080 DIGIT_SCAN_HL_DELAY EQU 1 F00:0081 BEEP_1K_DURATION EQU $0020 F00:0082 F00:0083 ; ==================================================================================== F00:0084 ; Definitions in RAM for use by the system F00:0085 ORG EXP_ROM_RAM_VAR_START F00:0086 F00:0087 ; Definition for Display buffer (data fields on right, address fields on left) F00:0088 DisplayBuffData RMB (DispBuffDataLen) F00:0089 DisplayBuffAddr RMB (DispBuffAddrLen) F00:0090 F00:0091 ; Definitions for holding data for interrupt driven clock F00:0092 TimeBuffBaseCount RMB 1 ; base level count for timer F00:0093 TimeBuffSeconds RMB 1 F00:0094 TimeBuffMinutes RMB 1 F00:0095 TimeBuffHours RMB 1 F00:0096 F00:0097 F00:0098 ; ==================================================================================== F00:0099 ; ==================================================================================== F00:0100 ; Define start of Expansion ROM area - ASCII text indicating firmware name and version F00:0101 ORG EXP_ROM_START F00:0102 F00:0103 string "Multitech Micro-Professor Test/ Demo ROM v1.1 (c) R.A.Coward 2017 " S02:00002000: 4D 75 6C 74 69 74 65 63 68 20 4D 69 63 72 6F 2D S02:00002010: 50 72 6F 66 65 73 73 6F 72 20 54 65 73 74 2F 20 F00:0104 db 00 S02:00002045: 00 F00:0105 F00:0106 ; ==================================================================================== F00:0107 ; ==================================================================================== F00:0108 ; Define start of Expansion ROM jump table area F00:0109 ; Each jump table entry is on a 4 byte boundary for user convenience when executing F00:0110 ORG EXP_ROM_JUMP_TAB F00:0111 F00:0112 ; EPROM checksum computation routines F00:0113 CALL_BaseMonitorRomChecksum F00:0114 JP EXEC_BaseMonitorRomChecksum S03:00002080: C3 00 21 F00:0115 NOP S03:00002083: 00 F00:0116 CALL_TopMonitorRomChecksum F00:0117 JP EXEC_TopMonitorRomChecksum S03:00002084: C3 09 21 F00:0118 NOP S03:00002087: 00 F00:0119 CALL_FullMonitorRomChecksum F00:0120 JP EXEC_FullMonitorRomChecksum S03:00002088: C3 12 21 F00:0121 NOP S03:0000208B: 00 F00:0122 CALL_ExpansionRomChecksum F00:0123 JP EXEC_ExpansionRomChecksum S03:0000208C: C3 1B 21 F00:0124 NOP S03:0000208F: 00 F00:0125 F00:0126 ; RAM memory test routines F00:0127 CALL_RamAddressTest F00:0128 JP EXEC_RamAddressTest S03:00002090: C3 4D 21 F00:0129 NOP S03:00002093: 00 F00:0130 CALL_RamPatternTest F00:0131 JP EXEC_RamPatternTest S03:00002094: C3 98 21 F00:0132 NOP S03:00002097: 00 F00:0133 F00:0134 ; Keyboard test routines F00:0135 CALL_KeyCodeDisplay F00:0136 JP EXEC_KeyCodeDisplay S03:00002098: C3 25 22 F00:0137 NOP S03:0000209B: 00 F00:0138 CALL_KeyCodeDisplayPos F00:0139 JP EXEC_KeyCodeDisplayPos S03:0000209C: C3 5B 22 F00:0140 NOP S03:0000209F: 00 F00:0141 F00:0142 ; Various routines to test the LED address/data display F00:0143 CALL_TestAllDisplaySegments F00:0144 JP EXEC_TestAllDisplaySegments S03:000020A0: C3 A7 22 F00:0145 NOP S03:000020A3: 00 F00:0146 CALL_TestEachDisplaySegment F00:0147 JP EXEC_TestEachDisplaySegment S03:000020A4: C3 CD 22 F00:0148 NOP S03:000020A7: 00 F00:0149 F00:0150 ; Interrupt driven electronic clock/counter and countdown timer F00:0151 CALL_IntDrivenClock F00:0152 JP EXEC_IntDrivenClock S03:000020A8: C3 F5 22 F00:0153 NOP S03:000020AB: 00 F00:0154 CALL_IntDrivenCountDown F00:0155 JP EXEC_IntDrivenCountDown S03:000020AC: C3 2C 23 F00:0156 NOP S03:000020AF: 00 F00:0157 F00:0158 ; Simple sound generator routines F00:0159 CALL_TelRingTone F00:0160 JP EXEC_TelRingTone S03:000020B0: C3 19 24 F00:0161 NOP S03:000020B3: 00 F00:0162 CALL_TelRingUKTone F00:0163 JP EXEC_TelRingUKTone S03:000020B4: C3 38 24 F00:0164 NOP S03:000020B7: 00 F00:0165 CALL_TelRingBusyTone F00:0166 JP EXEC_TelRingBusyTone S03:000020B8: C3 66 24 F00:0167 NOP S03:000020BB: 00 F00:0168 F00:0169 ; Simple electronic organ and music boxes F00:0170 CALL_ElectronicOrgan F00:0171 JP EXEC_ElectronicOrgan S03:000020BC: C3 7D 24 F00:0172 NOP S03:000020BF: 00 F00:0173 CALL_MusicBoxJingleBells F00:0174 JP EXEC_MusicBoxJingleBells S03:000020C0: C3 D4 24 F00:0175 NOP S03:000020C3: 00 F00:0176 CALL_MusicBoxGreenSleeves F00:0177 JP EXEC_MusicBoxGreenSleeves S03:000020C4: C3 DA 24 F00:0178 NOP S03:000020C7: 00 F00:0179 F00:0180 F00:0181 ; ==================================================================================== F00:0182 ; Interrupt vectors for this utility ROM - must be on 3 bit boundaries F00:0183 ORG EXP_ROM_INT_VECT1 F00:0184 DW ClockInterrupt S04:000020E0: C9 23 F00:0185 ORG EXP_ROM_INT_VECT2 F00:0186 DW CountDownInterrupt S05:000020F0: DD 23 F00:0187 F00:0188 ; ==================================================================================== F00:0189 ; Starting address for the utility ROM program code F00:0190 ORG EXP_ROM_CODE_START F00:0191 F00:0192 ; ==================================================================================== F00:0193 ; EPROM checksum computation routines, displaying result on the Address LED display. F00:0194 F00:0195 EXEC_BaseMonitorRomChecksum F00:0196 ; Setup base monitor ROM start and base size and continue to common code F00:0197 LD DE,MON_ROM_BASE_START ; Start address for base monitor ROM in DE S06:00002100: 11 00 00 F00:0198 LD BC,MON_ROM_BASE_SIZE ; Base image size for monitor rom in BC S06:00002103: 01 00 08 F00:0199 JP RomCheckCommon ; Jump to common code S06:00002106: C3 21 21 F00:0200 F00:0201 EXEC_TopMonitorRomChecksum F00:0202 ; Setup top monitor ROM start and top size and continue to common code F00:0203 LD DE,MON_ROM_TOP_START ; Start address for top monitor ROM in DE S06:00002109: 11 00 08 F00:0204 LD BC,MON_ROM_BASE_SIZE ; Top image size for monitor rom in BC S06:0000210C: 01 00 08 F00:0205 JP RomCheckCommon ; Jump to common code S06:0000210F: C3 21 21 F00:0206 F00:0207 EXEC_FullMonitorRomChecksum F00:0208 ; Setup base monitor ROM start and full size and continue to common code F00:0209 LD DE,MON_ROM_BASE_START ; Start address for base monitor ROM in DE S06:00002112: 11 00 00 F00:0210 LD BC,MON_ROM_FULL_SIZE ; Full image size for monitor rom in BC S06:00002115: 01 00 10 F00:0211 JP RomCheckCommon ; Jump to common code S06:00002118: C3 21 21 F00:0212 F00:0213 EXEC_ExpansionRomChecksum F00:0214 ; Setup the Expansion ROM start and base sise and continue to common code F00:0215 LD DE,EXP_ROM_START ; Start address for expansion ROM in DE S06:0000211B: 11 00 20 F00:0216 LD BC,EXP_ROM_BASE_SIZE ; Base image size for expansion rom in BC S06:0000211E: 01 00 08 F00:0217 F00:0218 ; .................................................................................... F00:0219 RomCheckCommon F00:0220 ; First clear down the entire display buffer, ready for updates, preserving registers F00:0221 PUSH BC S06:00002121: C5 F00:0222 CALL ClearDispBuffer S06:00002122: CD F9 21 F00:0223 POP BC S06:00002125: C1 F00:0224 ; now set the ROM image size and initialise the checksum to zero F00:0225 LD HL,$0000 ; Starting value for checksum in HL S06:00002126: 21 00 00 F00:0226 F00:0227 RomCheckLoop F00:0228 ; load in data from current location (DE) and add to checksum in HL F00:0229 PUSH BC ; preserve counter in BC S06:00002129: C5 F00:0230 LD A,(DE) ; load value from addr in DE S06:0000212A: 1A F00:0231 LD C,A ; transver value to BC S06:0000212B: 4F F00:0232 LD B,$00 ; S06:0000212C: 06 00 F00:0233 ADD HL,BC ; add BC to checksum in HL S06:0000212E: 09 F00:0234 POP BC ; restore counter in BC S06:0000212F: C1 F00:0235 ; now increment addr in DE, decrement the counter in BC and loop round until done F00:0236 INC DE ; increment address in DE S06:00002130: 13 F00:0237 DEC BC ; decrement counter in BC S06:00002131: 0B F00:0238 LD A,C ; check if counter zero S06:00002132: 79 F00:0239 OR B ; S06:00002133: B0 F00:0240 JP NZ,RomCheckLoop ; loop round until done S06:00002134: C2 29 21 F00:0241 F00:0242 ; Finished computing checksum in HL, so now transfer to the address display F00:0243 ; wait until key is pressed and show the code on the display F00:0244 EX DE,HL ; get checksum in DE S06:00002137: EB F00:0245 LD HL,DisplayBuffAddr ; set start of address field S06:00002138: 21 12 19 F00:0246 ; now increment counter and update on the display (HL is already correct) F00:0247 LD A,E ; set LSB in A register S06:0000213B: 7B F00:0248 CALL HEX7SG ; display the LSB on right hand addr S06:0000213C: CD 78 06 F00:0249 LD A,D ; set MSB in A register S06:0000213F: 7A F00:0250 CALL HEX7SG ; display the MSB on right hand addr S06:00002140: CD 78 06 F00:0251 ; Now update the display and effectively halt execution F00:0252 RomCheckEnd F00:0253 LD IX,DisplayBuffData ; set scan buffer start S06:00002143: DD 21 10 19 F00:0254 CALL SCAN ; update the display S06:00002147: CD FE 05 F00:0255 JP RomCheckEnd ; keep looping round S06:0000214A: C3 43 21 F00:0256 F00:0257 ; ==================================================================================== F00:0258 ; RAM Address line test routines for the 2K RAM in the unit. F00:0259 ; The test walks a single bit through the address bus, writing an incrementing counter F00:0260 ; to echa location. The data is then read back and compared; if any address line faults F00:0261 ; exist, higher addresses will be aliassed onto lower ones. F00:0262 ; If the test passes, then PASS is displayed, otherwise the fail address is displayed F00:0263 ; and then cycles between data displays showing the actual and expected data (DP lit). F00:0264 ; Note that this test should never interfere with the Monitor RAM and stack usage, as F00:0265 ; these are near the top of RAM, and the highest location written during the test is F00:0266 ; half way through the RAM at $1C00. Note that you would expect all lower order address F00:0267 ; lines to be OK because the monitor would not be able to run without it. F00:0268 F00:0269 EXEC_RamAddressTest F00:0270 ; First clear down the entire display buffer, ready for updates F00:0271 CALL ClearDispBuffer S06:0000214D: CD F9 21 F00:0272 ; Set the RAM starting address, and write out zero data to the first location F00:0273 LD HL,RAM_MEM_START ; Start address for expansion RAM in HL S06:00002150: 21 00 18 F00:0274 XOR A ; zero the first byte S06:00002153: AF F00:0275 LD (HL),A ; store in first location S06:00002154: 77 F00:0276 ; now set the starting data and offset seed value and proceed to the loop F00:0277 LD BC,$0001 ; set starting offset in BC S06:00002155: 01 01 00 F00:0278 LD D,$01 ; staring seed value in D S06:00002158: 16 01 F00:0279 F00:0280 RamAddressWriteLoop F00:0281 ; Compute the offset location and write data out to it, preserving base address F00:0282 PUSH HL ; preserve base in HL - IMPORTANT! S06:0000215A: E5 F00:0283 ADD HL,BC ; add on offset in BC S06:0000215B: 09 F00:0284 LD (HL),D ; write data to the memory location S06:0000215C: 72 F00:0285 POP HL ; restore base in HL - IMPORTANT! S06:0000215D: E1 F00:0286 ; Now shift the offset in BC up by one bit (16 bit shift left) F00:0287 SLA C ; shift LSB up one bit, overflow into carry S06:0000215E: CB 21 F00:0288 RL B ; shift MSB up one bit, carry into bit 0 S06:00002160: CB 10 F00:0289 ; now check if we have hit the limit by comparing to end stop F00:0290 PUSH HL ; preserve base in HL - IMPORTANT! S06:00002162: E5 F00:0291 LD HL,RAM_MEM_SIZE ; set the RAM memory size offset S06:00002163: 21 00 08 F00:0292 LD A,B ; compare upper bytes only S06:00002166: 78 F00:0293 CP H ; S06:00002167: BC F00:0294 POP HL ; restore base in HL - IMPORTANT! S06:00002168: E1 F00:0295 JR NC,RamAddrWriteFinished ; finished write once hit limit S06:00002169: 30 03 F00:0296 ; Now increment the data value and loop round to next cycle F00:0297 INC D ; increment the data value to be written S06:0000216B: 14 F00:0298 JR RamAddressWriteLoop ; loop round to write next data S06:0000216C: 18 EC F00:0299 F00:0300 RamAddrWriteFinished F00:0301 ; Finished the write cycle, so now read back all the data, checking for matches F00:0302 ; Start by reading in the base location and checking for a match F00:0303 LD BC,$0000 ; set starting offset in BC S06:0000216E: 01 00 00 F00:0304 LD D,$00 ; staring value for comparison in D S06:00002171: 16 00 F00:0305 LD A,(HL) ; read in the zero location S06:00002173: 7E F00:0306 CP D ; check if we have a match S06:00002174: BA F00:0307 JR NZ,RamAddrTestFail ; jump to failure if no match S06:00002175: 20 1D F00:0308 ; Now setup the data for the read address loop test F00:0309 LD BC,$0001 ; set starting offset in BC S06:00002177: 01 01 00 F00:0310 LD D,$01 ; starting seed value in D S06:0000217A: 16 01 F00:0311 F00:0312 RamAddressReadLoop F00:0313 ; Compute the offset location and read data in from it, preserving base address F00:0314 PUSH HL ; preserve base in HL - IMPORTANT! S06:0000217C: E5 F00:0315 ADD HL,BC ; add on offset in BC S06:0000217D: 09 F00:0316 LD A,(HL) ; read data from the memory location S06:0000217E: 7E F00:0317 POP HL ; restore base in HL - IMPORTANT! S06:0000217F: E1 F00:0318 ; Compare the read data in A with the expected data in D F00:0319 CP D ; compare read with expected S06:00002180: BA F00:0320 JR NZ,RamAddrTestFail ; jump to failure if no match S06:00002181: 20 11 F00:0321 ; Now shift the offset in BC up by one bit (16 bit shift left) F00:0322 SLA C ; shift LSB up one bit, overflow into carry S06:00002183: CB 21 F00:0323 RL B ; shift MSB up one bit, carry into bit 0 S06:00002185: CB 10 F00:0324 ; now check if we have hit the limit by comparing to end stop F00:0325 PUSH HL ; preserve base in HL - IMPORTANT! S06:00002187: E5 F00:0326 LD HL,RAM_MEM_SIZE ; set the RAM memory size offset S06:00002188: 21 00 08 F00:0327 LD A,B ; compare upper bytes only S06:0000218B: 78 F00:0328 CP H ; S06:0000218C: BC F00:0329 POP HL ; restore base in HL - IMPORTANT! S06:0000218D: E1 F00:0330 JP NC,RamTestPassDisplay ; PASSED read cycles once hit limit S06:0000218E: D2 B3 21 F00:0331 ; Now increment the data value and loop round to next cycle F00:0332 INC D ; increment the data value to be written S06:00002191: 14 F00:0333 JR RamAddressReadLoop ; loop round to read next data S06:00002192: 18 E8 F00:0334 F00:0335 RamAddrTestFail F00:0336 ; test has failed, so munge the data into the required format for the generic display F00:0337 ADD HL,BC ; add on offset in BC to get address S06:00002194: 09 F00:0338 JP RamTestFailDisplay ; jump to generic failure display S06:00002195: C3 C3 21 F00:0339 F00:0340 F00:0341 ; ==================================================================================== F00:0342 ; Non-destructive RAM pattern test routines for the 2K RAM in the unit F00:0343 ; The test operates like the RAM test described in the Micro Professor user guide, F00:0344 ; non-destructively testing ALL RAM locations, but will display clearer indications. F00:0345 ; If the test passes, then PASS is displayed, otherwise the fail address is displayed F00:0346 ; and then cycles between data displays showing the actual and expected data (DP lit). F00:0347 ; The test will check that all bits of all RAM locations can store either 1 or 0, F00:0348 ; but will NOT detect address line faults and may not detect obscure faults that F00:0349 ; only show up with certain patterns. Note that since the RAM test is non-destructive, F00:0350 ; it can safely be run on all locations, including the stack and monitor area. F00:0351 F00:0352 EXEC_RamPatternTest F00:0353 ; First clear down the entire display buffer, ready for updates F00:0354 CALL ClearDispBuffer S06:00002198: CD F9 21 F00:0355 ; Set the RAM starting address and RAM size to test F00:0356 LD HL,RAM_MEM_START ; Start address for expansion RAM in HL S06:0000219B: 21 00 18 F00:0357 LD BC,RAM_MEM_SIZE ; set the RAM memory size in BC S06:0000219E: 01 00 08 F00:0358 F00:0359 RamPattTestLoop F00:0360 ; Check the current location for non-destructive writability, and check result F00:0361 CALL RAMCHK ; non-destructive location test S06:000021A1: CD F6 05 F00:0362 JR NZ,RamPattTestFail ; jump out if a failure detected S06:000021A4: 20 08 F00:0363 ; Decrement counter, increment address and loop round until complete F00:0364 CPI ; dec counter, inc addr and check if done S06:000021A6: ED A1 F00:0365 JP PE,RamPattTestLoop ; loop round until done S06:000021A8: EA A1 21 F00:0366 ; Test has got to the end, so indicate that it has passed F00:0367 JP RamTestPassDisplay ; jump to generic pass display S06:000021AB: C3 B3 21 F00:0368 F00:0369 RamPattTestFail F00:0370 ; test has failed, so munge the data into the required format for the generic display F00:0371 LD D,A ; load expected data into D S06:000021AE: 57 F00:0372 LD A,(HL) ; load actual data into A S06:000021AF: 7E F00:0373 JP RamTestFailDisplay ; jump to generic failure display S06:000021B0: C3 C3 21 F00:0374 F00:0375 ; ==================================================================================== F00:0376 ; Generic PASS display used by all the RAM tests - just displays PASS on the address LEDs F00:0377 F00:0378 RamTestPassDisplay F00:0379 ; RAM Test has passed, so report this on the address display F00:0380 LD IX,PassDisplayData ; set scan buffer start S06:000021B3: DD 21 BD 21 F00:0381 CALL SCAN ; update the display S06:000021B7: CD FE 05 F00:0382 JP RamTestPassDisplay ; loop round indefinitely S06:000021BA: C3 B3 21 F00:0383 F00:0384 ; static data for the pass display (covers all 6 digits) F00:0385 PassDisplayData F00:0386 DB $00 ; blank S06:000021BD: 00 F00:0387 DB $00 ; blank S06:000021BE: 00 F00:0388 DB $AE ; S S06:000021BF: AE F00:0389 DB $AE ; S S06:000021C0: AE F00:0390 DB $3F ; A S06:000021C1: 3F F00:0391 DB $1F ; P S06:000021C2: 1F F00:0392 F00:0393 ; ------------------------------------------------------------------------------------ F00:0394 ; Generic failure display for RAM address and pattern fill tests F00:0395 ; On entry, HL contains failed address location F00:0396 ; Register A contains the read data and D contains the expected data F00:0397 F00:0398 RamTestFailDisplay F00:0399 ; Store actual result in E, leaving expected data in D, and preserve on stack F00:0400 LD E,A ; store actual result in E S06:000021C3: 5F F00:0401 PUSH DE ; preserve D and E registers S06:000021C4: D5 F00:0402 ; Test has failed, so first report failed address (in HL) on LED address display F00:0403 EX DE,HL ; transfer HL to DE S06:000021C5: EB F00:0404 LD HL,DisplayBuffAddr ; set display buffer address S06:000021C6: 21 12 19 F00:0405 ; Transfer the contents of DE onto the address display F00:0406 LD A,E ; set LSB in A register S06:000021C9: 7B F00:0407 CALL HEX7SG ; display the LSB on right hand addr S06:000021CA: CD 78 06 F00:0408 LD A,D ; set MSB in A register S06:000021CD: 7A F00:0409 CALL HEX7SG ; display the MSB on right hand addr S06:000021CE: CD 78 06 F00:0410 ; set the decimal point at the end of the address field F00:0411 LD HL,DisplayBuffAddr ; set display buffer address S06:000021D1: 21 12 19 F00:0412 SET 6,(HL) ; set the decimal point S06:000021D4: CB F6 F00:0413 F00:0414 RamTestFailDispLoop F00:0415 ; First display the actual result on the data display without the dot set F00:0416 POP DE ; restore D and E registers S06:000021D6: D1 F00:0417 PUSH DE ; preserve D and E registers S06:000021D7: D5 F00:0418 LD HL,DisplayBuffData ; set start of data field S06:000021D8: 21 10 19 F00:0419 LD A,E ; set actual result in A register S06:000021DB: 7B F00:0420 CALL HEX7SG ; display the LSB on right hand addr S06:000021DC: CD 78 06 F00:0421 ; Now wait for a delay while scanning and restore registers from the stack F00:0422 LD B,FAIL_MSG_HL_DELAY ; high level delay in B S06:000021DF: 06 03 F00:0423 CALL WaitLongDelayScan ; wait for delay while scanning S06:000021E1: CD 0F 22 F00:0424 ; Now display the expected result on the data display with the dot set F00:0425 POP DE ; restore D and E registers S06:000021E4: D1 F00:0426 PUSH DE ; preserve D and E registers S06:000021E5: D5 F00:0427 LD HL,DisplayBuffData ; set start of data field S06:000021E6: 21 10 19 F00:0428 LD A,D ; set expected result in A register S06:000021E9: 7A F00:0429 CALL HEX7SG ; display the LSB on right hand addr S06:000021EA: CD 78 06 F00:0430 ; set the decimal point on the data field, indicating expected result F00:0431 LD HL,DisplayBuffData ; set start of data field S06:000021ED: 21 10 19 F00:0432 SET 6,(HL) ; set the decimal point S06:000021F0: CB F6 F00:0433 ; Now wait for the delay while scanning , preserve registers and loop round F00:0434 LD B,FAIL_MSG_HL_DELAY ; high level delay in B S06:000021F2: 06 03 F00:0435 CALL WaitLongDelayScan ; wait for delay while scanning S06:000021F4: CD 0F 22 F00:0436 JR RamTestFailDispLoop ; loop round to fail display S06:000021F7: 18 DD F00:0437 F00:0438 F00:0439 ; ==================================================================================== F00:0440 ; Utility function to clear down the display buffer F00:0441 F00:0442 ClearDispBuffer F00:0443 ; First clear down the entire display buffer, ready for updates F00:0444 LD B,DispBuffFullLen S06:000021F9: 06 06 F00:0445 LD HL,DisplayBuffData S06:000021FB: 21 10 19 F00:0446 DispBuffClrLoop F00:0447 LD (HL),$00 S06:000021FE: 36 00 F00:0448 INC HL S06:00002200: 23 F00:0449 DJNZ DispBuffClrLoop S06:00002201: 10 FB F00:0450 RET S06:00002203: C9 F00:0451 F00:0452 ; ==================================================================================== F00:0453 ; Long delay routine, B specifies number of cycles - most registers corrupted F00:0454 F00:0455 WaitLongDelay F00:0456 ; Set the inner delay loop value and loop round until done F00:0457 LD DE,MAX_BASE_DELAY_VAL ; load delay value and call delay S06:00002204: 11 FF FF F00:0458 WaitDelayInnerLoop F00:0459 ; perform the inner delay loop using counter in DE F00:0460 DEC DE S06:00002207: 1B F00:0461 LD A,D S06:00002208: 7A F00:0462 OR E S06:00002209: B3 F00:0463 JR NZ,WaitDelayInnerLoop S06:0000220A: 20 FB F00:0464 ; decrement high level counter in B and loop round until done F00:0465 DJNZ WaitLongDelay S06:0000220C: 10 F6 F00:0466 RET S06:0000220E: C9 F00:0467 F00:0468 ; ==================================================================================== F00:0469 ; Long delay routine while continuing to scan the display F00:0470 ; B specifies number of cycles - most registers corrupted F00:0471 F00:0472 WaitLongDelayScan F00:0473 ; Set the inner delay loop value and loop round until done F00:0474 LD DE,SCAN_BASE_DELAY_VAL ; load delay value and call delay S06:0000220F: 11 20 00 F00:0475 WaitDelayScanInnerLoop F00:0476 ; now perform the display update without waiting for a key press F00:0477 PUSH BC S06:00002212: C5 F00:0478 PUSH DE S06:00002213: D5 F00:0479 LD IX,DisplayBuffData ; set scan buffer start S06:00002214: DD 21 10 19 F00:0480 CALL SCAN1 ; update display data S06:00002218: CD 24 06 F00:0481 POP DE S06:0000221B: D1 F00:0482 POP BC S06:0000221C: C1 F00:0483 ; perform the inner delay loop using counter in DE F00:0484 DEC DE S06:0000221D: 1B F00:0485 LD A,D S06:0000221E: 7A F00:0486 OR E S06:0000221F: B3 F00:0487 JR NZ,WaitDelayScanInnerLoop S06:00002220: 20 F0 F00:0488 ; decrement high level counter in B and loop round until done F00:0489 DJNZ WaitLongDelayScan S06:00002222: 10 EB F00:0490 RET S06:00002224: C9 F00:0491 F00:0492 F00:0493 ; ==================================================================================== F00:0494 ; Display the key code of the pressed key, along with an incrementing keypress counter F00:0495 ; The initial display shows the zero counter and dashes for the key pressed. F00:0496 ; This does NOT test the User Key or INTR key; for these, use the key code position F00:0497 ; routine instead, as this uses the non-blocking SCAN1 method for detecting key presses. F00:0498 F00:0499 EXEC_KeyCodeDisplay F00:0500 ; Setup the initial display with dashes for the key pressed F00:0501 LD HL,DisplayBuffData ; set start of data field S06:00002225: 21 10 19 F00:0502 LD (HL),$02 ; display dash S06:00002228: 36 02 F00:0503 INC HL ; select next digit S06:0000222A: 23 F00:0504 LD (HL),$02 ; display dash S06:0000222B: 36 02 F00:0505 INC HL ; select next digit S06:0000222D: 23 F00:0506 ; Now zero the counter and put it on the address display F00:0507 LD DE,$00 ; zero the key counter S06:0000222E: 11 00 00 F00:0508 LD A,E ; set LSB in A register S06:00002231: 7B F00:0509 CALL HEX7SG ; display the LSB on right hand addr S06:00002232: CD 78 06 F00:0510 LD A,D ; set MSB in A register S06:00002235: 7A F00:0511 CALL HEX7SG ; display the MSB on right hand addr S06:00002236: CD 78 06 F00:0512 ; Finally, set decimal point at end of the address display F00:0513 LD HL,DisplayBuffAddr ; last digit of address display S06:00002239: 21 12 19 F00:0514 SET 6,(HL) ; set the decimal point S06:0000223C: CB F6 F00:0515 ; setup the address for the display scan and enter main loop F00:0516 LD IX,DisplayBuffData ; set scan buffer start S06:0000223E: DD 21 10 19 F00:0517 F00:0518 KeyCodeMainLoop F00:0519 ; wait until key is pressed and show the code on the display (from A reg) F00:0520 CALL SCAN ; wait for key pressed S06:00002242: CD FE 05 F00:0521 LD HL,DisplayBuffData ; set start of data field S06:00002245: 21 10 19 F00:0522 CALL HEX7SG ; display data from A register S06:00002248: CD 78 06 F00:0523 ; now increment counter and update on the display (HL is already correct) F00:0524 INC DE ; increment counter S06:0000224B: 13 F00:0525 LD A,E ; set LSB in A register S06:0000224C: 7B F00:0526 CALL HEX7SG ; display the LSB on right hand addr S06:0000224D: CD 78 06 F00:0527 LD A,D ; set MSB in A register S06:00002250: 7A F00:0528 CALL HEX7SG ; display the MSB on right hand addr S06:00002251: CD 78 06 F00:0529 ; Finally, set decimal point at end of the address display F00:0530 LD HL,DisplayBuffAddr ; last digit of address display S06:00002254: 21 12 19 F00:0531 SET 6,(HL) ; set the decimal point S06:00002257: CB F6 F00:0532 JR KeyCodeMainLoop ; loop round indefinitely S06:00002259: 18 E7 F00:0533 F00:0534 ; ==================================================================================== F00:0535 ; Display the position code of the pressed key, along with an incrementing keypress counter. F00:0536 ; The initial display shows the zero counter and dashes for the key pressed. F00:0537 ; This also handles the User Key, displaying "UK." whenever it is pressed - the F00:0538 ; counter is NOT incremented for the User Key. F00:0539 ; F00:0540 ; Global interrupts are also enabled, allowing the INTR key to operate the same F00:0541 ; way as the MONI key (enter the monitor). Providing a more sophisticated handler F00:0542 ; would be undesirable, as it would require overriding of the default monitor handler, F00:0543 ; and this is only restored on power-up, rather than just the RESET key being pressed. F00:0544 ; F00:0545 ; To test the MONI or INTR keys, press one of them and check that the monitor is entered; F00:0546 ; the address and data displays should change, and both data display decimal points F00:0547 ; should be lit. Then simply press the GO key and the unit should beep and resume the F00:0548 ; key test program. F00:0549 F00:0550 EXEC_KeyCodeDisplayPos F00:0551 ; Setup the initial display with dashes for the key pressed F00:0552 LD HL,DisplayBuffData ; set start of data field S06:0000225B: 21 10 19 F00:0553 LD (HL),$02 ; display dash S06:0000225E: 36 02 F00:0554 INC HL ; select next digit S06:00002260: 23 F00:0555 LD (HL),$02 ; display dash S06:00002261: 36 02 F00:0556 INC HL ; select next digit S06:00002263: 23 F00:0557 ; Now zero the counter and put it on the address display F00:0558 LD DE,$00 ; zero the key counter S06:00002264: 11 00 00 F00:0559 LD A,E ; set LSB in A register S06:00002267: 7B F00:0560 CALL HEX7SG ; display the LSB on right hand addr S06:00002268: CD 78 06 F00:0561 LD A,D ; set MSB in A register S06:0000226B: 7A F00:0562 CALL HEX7SG ; display the MSB on right hand addr S06:0000226C: CD 78 06 F00:0563 ; Now set decimal point at end of the address display F00:0564 LD HL,DisplayBuffAddr ; last digit of address display S06:0000226F: 21 12 19 F00:0565 SET 6,(HL) ; set the decimal point S06:00002272: CB F6 F00:0566 ; Setup the address for the display scan and enter main loop F00:0567 LD IX,DisplayBuffData ; set scan buffer start S06:00002274: DD 21 10 19 F00:0568 F00:0569 KeyCodePosMainLoop F00:0570 ; Enable global interrupts to allow the INTR key to trigger the monitor F00:0571 EI ; enable global interrupts S06:00002278: FB F00:0572 ; first check if the USER key has been pressed (direct connection to bit 6 on PIO) F00:0573 IN A,(KIN) ; read raw keyboard input S06:00002279: DB 00 F00:0574 BIT 6,A ; check user key input S06:0000227B: CB 77 F00:0575 JR NZ,KeyCodePosCkeckKey ; skip if NOT pressed S06:0000227D: 20 08 F00:0576 F00:0577 ; USER key has been pressed, so set special "UK." display on the Data field F00:0578 LD HL,DisplayBuffData ; set start of data field S06:0000227F: 21 10 19 F00:0579 LD (HL),$D7 ; display K character, DP set S06:00002282: 36 D7 F00:0580 INC HL ; select next digit S06:00002284: 23 F00:0581 LD (HL),$B5 ; display U character S06:00002285: 36 B5 F00:0582 F00:0583 KeyCodePosCkeckKey F00:0584 ; check if a key is pressed and loop round if not F00:0585 CALL SCAN1 ; get any key pressed S06:00002287: CD 24 06 F00:0586 JR C,KeyCodePosMainLoop ; loop round if NOT pressed S06:0000228A: 38 EC F00:0587 F00:0588 ; key has been pressed, so display its postion code F00:0589 LD HL,DisplayBuffData ; set start of data field S06:0000228C: 21 10 19 F00:0590 CALL HEX7SG ; display data from A register S06:0000228F: CD 78 06 F00:0591 ; now increment counter and update on the display (HL is already correct) F00:0592 INC DE ; increment counter S06:00002292: 13 F00:0593 LD A,E ; set LSB in A register S06:00002293: 7B F00:0594 CALL HEX7SG ; display the LSB on right hand addr S06:00002294: CD 78 06 F00:0595 LD A,D ; set MSB in A register S06:00002297: 7A F00:0596 CALL HEX7SG ; display the MSB on right hand addr S06:00002298: CD 78 06 F00:0597 ; Finally, set decimal point at end of the address display F00:0598 LD HL,DisplayBuffAddr ; last digit of address display S06:0000229B: 21 12 19 F00:0599 SET 6,(HL) ; set the decimal point S06:0000229E: CB F6 F00:0600 F00:0601 KeyCodePosWaitLoop F00:0602 ; keep looping around until the key is no longer pressed F00:0603 CALL SCAN1 ; get any key pressed S06:000022A0: CD 24 06 F00:0604 JR C,KeyCodePosMainLoop ; jump to main loop if NOT pressed S06:000022A3: 38 D3 F00:0605 JR KeyCodePosWaitLoop ; otherwise keep looping round here S06:000022A5: 18 F9 F00:0606 F00:0607 ; ==================================================================================== F00:0608 ; All segment disply "lamp test" routine, including decimal points F00:0609 F00:0610 EXEC_TestAllDisplaySegments F00:0611 ; set all segments of all displays on, by writing 8's to every digit F00:0612 LD HL,DisplayBuffData ; set start of data field S06:000022A7: 21 10 19 F00:0613 LD A,$88 ; set all segments on S06:000022AA: 3E 88 F00:0614 CALL HEX7SG ; display data from A register S06:000022AC: CD 78 06 F00:0615 LD A,$88 ; set all segments on S06:000022AF: 3E 88 F00:0616 CALL HEX7SG ; display data from A register S06:000022B1: CD 78 06 F00:0617 LD A,$88 ; set all segments on S06:000022B4: 3E 88 F00:0618 CALL HEX7SG ; display data from A register S06:000022B6: CD 78 06 F00:0619 ; Finally, set decimal point on every single display F00:0620 LD HL,DisplayBuffData ; set start of data field S06:000022B9: 21 10 19 F00:0621 LD B,$06 ; set loop counter S06:000022BC: 06 06 F00:0622 DispLampTestDpLoop F00:0623 SET 6,(HL) ; set the decimal point S06:000022BE: CB F6 F00:0624 INC HL ; move to next digit S06:000022C0: 23 F00:0625 DJNZ DispLampTestDpLoop ; loop round until done S06:000022C1: 10 FB F00:0626 F00:0627 ; Now update the display and effectively halt execution F00:0628 DispLampTestEnd F00:0629 LD IX,DisplayBuffData ; set scan buffer start S06:000022C3: DD 21 10 19 F00:0630 CALL SCAN ; update the display S06:000022C7: CD FE 05 F00:0631 JP DispLampTestEnd ; keep looping round S06:000022CA: C3 C3 22 F00:0632 F00:0633 ; ==================================================================================== F00:0634 ; Low level display sequencing test, including decimal points F00:0635 ; This cycles through each segment in turn on each digit, checking they are F00:0636 ; individually addressible. F00:0637 F00:0638 EXEC_TestEachDisplaySegment F00:0639 ; First clear down the entire display buffer, and set digit selector to left digit F00:0640 CALL ClearDispBuffer S06:000022CD: CD F9 21 F00:0641 LD B,$06 ; set the digit select counter S06:000022D0: 06 06 F00:0642 F00:0643 TestEachSegOuterLoop F00:0644 ; Setect the digit address to update using the stored digit counter F00:0645 PUSH BC ; preserve digit counter S06:000022D2: C5 F00:0646 LD HL,DisplayBuffData ; set start of data field S06:000022D3: 21 10 19 F00:0647 LD C,B ; move counter to LSB in C S06:000022D6: 48 F00:0648 DEC C ; and decrement LSB S06:000022D7: 0D F00:0649 LD B,$00 ; set the MSB for digit selector S06:000022D8: 06 00 F00:0650 ADD HL,BC ; add on the digit offset S06:000022DA: 09 F00:0651 POP BC ; restore digit counter S06:000022DB: C1 F00:0652 ; set segment selector to the first segment to illuminate F00:0653 LD D,$01 ; start the segment selector S06:000022DC: 16 01 F00:0654 F00:0655 TestEachSegInnderLoop F00:0656 ; Output the currently selected digit data to the display digit F00:0657 LD A,D ; obtain the digit data S06:000022DE: 7A F00:0658 LD (HL),D ; store in selected digit S06:000022DF: 72 F00:0659 ; Now run the scan deley routine to show the updated display for a short time F00:0660 PUSH HL ; preserve digit address S06:000022E0: E5 F00:0661 PUSH BC ; preserve digit selector S06:000022E1: C5 F00:0662 PUSH DE ; preserve segment selector S06:000022E2: D5 F00:0663 LD B,DIGIT_SCAN_HL_DELAY ; high level delay in B S06:000022E3: 06 01 F00:0664 CALL WaitLongDelayScan ; wait for delay while scanning S06:000022E5: CD 0F 22 F00:0665 POP DE ; restore segment selector S06:000022E8: D1 F00:0666 POP BC ; restore digit selector S06:000022E9: C1 F00:0667 POP HL ; restore digit selector S06:000022EA: E1 F00:0668 ; Now shift up the segment selector and check if reached the end F00:0669 SLA D ; shift up digit select S06:000022EB: CB 22 F00:0670 JR NC,TestEachSegInnderLoop ; loop round to display this S06:000022ED: 30 EF F00:0671 F00:0672 ; Finished updating this digit, so write out the data to ensure it goes blank F00:0673 LD (HL),D ; store in selected digit S06:000022EF: 72 F00:0674 ; Now decrement the digit selector and loop round until all digits done F00:0675 DJNZ TestEachSegOuterLoop ; loop round to updatre next digit S06:000022F0: 10 E0 F00:0676 F00:0677 ; finished all digits, so start off from the beginning again F00:0678 JP EXEC_TestEachDisplaySegment S06:000022F2: C3 CD 22 F00:0679 F00:0680 F00:0681 ; ==================================================================================== F00:0682 ; Interrupt driven clock - main initialisation and runtime code F00:0683 ; On startup, displays all zeros, and allows the user to set it by pressing F00:0684 ; the number keys - this shifts in the starting count across the display F00:0685 ; Once the GO key is pressed, the clock starts running. Note that there is F00:0686 ; no protection against entering "invalid" hours, minutes or seconds, but the F00:0687 ; system will cope with these, and they will self-correct eventually. F00:0688 F00:0689 EXEC_IntDrivenClock F00:0690 ; Clear down all the time element stores, to start with a zero display F00:0691 CALL ClearTimeBuffers ; clear all time buffers S06:000022F5: CD F1 23 F00:0692 F00:0693 IntClockSetLoop F00:0694 ; Scan the keyboard checking for key presses, and process accordingly F00:0695 LD IX,DisplayBuffData ; set display address S06:000022F8: DD 21 10 19 F00:0696 CALL SCAN ; scan until key pressed S06:000022FC: CD FE 05 F00:0697 CP $12 ; check for the GO key S06:000022FF: FE 12 F00:0698 JR Z,IntDrivenClockRun ; start clock going if so S06:00002301: 28 07 F00:0699 ; check if number key, and if so, shift it into the time buffers F00:0700 CP $0A ; check if number key S06:00002303: FE 0A F00:0701 CALL C,ClockShiftInDigit ; shift in the digit if so S06:00002305: DC FF 23 F00:0702 JR IntClockSetLoop ; loop round for next keypress S06:00002308: 18 EE F00:0703 F00:0704 IntDrivenClockRun F00:0705 ; issue a beep to indicate that the clock has started running F00:0706 LD HL,BEEP_1K_DURATION S06:0000230A: 21 20 00 F00:0707 CALL TONE1K S06:0000230D: CD DE 05 F00:0708 ; load in high ROM address to top level interrupt register F00:0709 LD A,$20 ; set top part of ISR S06:00002310: 3E 20 F00:0710 LD I,A ; S06:00002312: ED 47 F00:0711 ; now program the CTC with the channel control word F00:0712 LD A,$B5 S06:00002314: 3E B5 F00:0713 OUT (CTC0),A S06:00002316: D3 40 F00:0714 ; now program the constant register F00:0715 LD A,$20 S06:00002318: 3E 20 F00:0716 OUT (CTC0),A S06:0000231A: D3 40 F00:0717 ; finally, load the low part of the interrupt vector register F00:0718 LD A,EXP_ROM_INT_VECT1_BASE ; corresponds to vector address S06:0000231C: 3E E0 F00:0719 OUT (CTC0),A S06:0000231E: D3 40 F00:0720 ; set interrupt mode 2 and enable interrupts F00:0721 IM 2 S06:00002320: ED 5E F00:0722 EI S06:00002322: FB F00:0723 F00:0724 IntClockMainLoop F00:0725 ; Loop round, calling the scan routine to update the display F00:0726 LD IX,DisplayBuffData S06:00002323: DD 21 10 19 F00:0727 CALL SCAN S06:00002327: CD FE 05 F00:0728 JR IntClockMainLoop S06:0000232A: 18 F7 F00:0729 F00:0730 F00:0731 ; ==================================================================================== F00:0732 ; Interrupt driven countdown timer - main initialisation and runtime code F00:0733 ; On startup, displays all zeros, and allows the user to set it by pressing F00:0734 ; the number keys - this shifts in the starting count across the display F00:0735 ; Once the GO key is pressed, the timer starts running. Note that there is F00:0736 ; no protection against entering "invalid" hours, minutes or seconds, but the F00:0737 ; system will cope with these, and they will self-correct eventually. F00:0738 ; Once the countdown has reached zero, will emit repeated beeping tones F00:0739 F00:0740 EXEC_IntDrivenCountDown F00:0741 ; Clear down all the time element stores, to start with a zero display F00:0742 CALL ClearTimeBuffers ; clear all time buffers S06:0000232C: CD F1 23 F00:0743 F00:0744 IntCountDownSetLoop F00:0745 ; Scan the keyboard checking for key presses, and process accordingly F00:0746 LD IX,DisplayBuffData ; set display address S06:0000232F: DD 21 10 19 F00:0747 CALL SCAN ; scan until key pressed S06:00002333: CD FE 05 F00:0748 CP $12 ; check for the GO key S06:00002336: FE 12 F00:0749 JR Z,IntDrivenCountDownRun ; start clock going if so S06:00002338: 28 07 F00:0750 ; check if number key, and if so, shift it into the time buffers F00:0751 CP $0A ; check if number key S06:0000233A: FE 0A F00:0752 CALL C,ClockShiftInDigit ; shift in the digit if so S06:0000233C: DC FF 23 F00:0753 JR IntCountDownSetLoop ; loop round for next keypress S06:0000233F: 18 EE F00:0754 F00:0755 IntDrivenCountDownRun F00:0756 ; issue a beep to indicate that the clock has started running F00:0757 LD HL,BEEP_1K_DURATION S06:00002341: 21 20 00 F00:0758 CALL TONE1K S06:00002344: CD DE 05 F00:0759 ; load in high ROM address to top level interrupt register F00:0760 LD A,$20 ; set top part of ISR S06:00002347: 3E 20 F00:0761 LD I,A ; S06:00002349: ED 47 F00:0762 ; now program the CTC with the channel control word F00:0763 LD A,$B5 S06:0000234B: 3E B5 F00:0764 OUT (CTC0),A S06:0000234D: D3 40 F00:0765 ; now program the constant register F00:0766 LD A,$20 S06:0000234F: 3E 20 F00:0767 OUT (CTC0),A S06:00002351: D3 40 F00:0768 ; finally, load the low part of the interrupt vector register F00:0769 LD A,EXP_ROM_INT_VECT2_BASE ; corresponds to vector address S06:00002353: 3E F0 F00:0770 OUT (CTC0),A S06:00002355: D3 40 F00:0771 ; set interrupt mode 2 and enable interrupts F00:0772 IM 2 S06:00002357: ED 5E F00:0773 EI S06:00002359: FB F00:0774 F00:0775 IntCountDownMainLoop F00:0776 ; Loop round, calling the scan routine to update the display F00:0777 LD IX,DisplayBuffData ; set display buffer start S06:0000235A: DD 21 10 19 F00:0778 CALL SCAN1 ; scan display WITHOUT waiting S06:0000235E: CD 24 06 F00:0779 ; Now check if all the countdown timer values have hit zero F00:0780 LD HL,TimeBuffSeconds ; start at seconds count S06:00002361: 21 17 19 F00:0781 LD B,$03 ; scan through all time data S06:00002364: 06 03 F00:0782 IntCountDownCheckZero F00:0783 LD A,(HL) ; load in the selected value S06:00002366: 7E F00:0784 OR A ; check if value is zero S06:00002367: B7 F00:0785 JR NZ,IntCountDownMainLoop ; jump to main loop if NOT zero S06:00002368: 20 F0 F00:0786 INC HL ; move on to next time value S06:0000236A: 23 F00:0787 DJNZ IntCountDownCheckZero ; loop round until done S06:0000236B: 10 F9 F00:0788 ; countdown has hit zero, so disable interrupts (freeze the count) and issue beeps F00:0789 DI ; prevent further counting S06:0000236D: F3 F00:0790 JP EXEC_TelRingBusyTone ; go to the beeping routine S06:0000236E: C3 66 24 F00:0791 F00:0792 ; ------------------------------------------------------------------------------------ F00:0793 ; Special table for defining maximum time limits for clock F00:0794 MaxTimeTable F00:0795 DB $DA ; maximum base count S06:00002371: DA F00:0796 DB $60 ; maximum seconds count S06:00002372: 60 F00:0797 DB $60 ; maximum minutes count S06:00002373: 60 F00:0798 DB $24 ; maximum hours count S06:00002374: 24 F00:0799 F00:0800 ; ------------------------------------------------------------------------------------ F00:0801 ; Special table for defining time limit and reload values for countdown F00:0802 CountDownTimeTable F00:0803 DB $DA ; maximum base count (incrementing) S06:00002375: DA F00:0804 DB $59 ; reload seconds count S06:00002376: 59 F00:0805 DB $59 ; reload minutes count S06:00002377: 59 F00:0806 DB $23 ; reload hours count S06:00002378: 23 F00:0807 F00:0808 ; ------------------------------------------------------------------------------------ F00:0809 ; Interrupt driven clock - update time buffer F00:0810 ; Base time element is incremented linearly; hours,mins,secs decimally F00:0811 ; All are compared to the max time table values; exit if hit limit F00:0812 F00:0813 UpdateTimeBufferClock F00:0814 ; Setup the parameters for updating the time elements, using maximum count table F00:0815 LD DE,TimeBuffBaseCount ; set base level counter S06:00002379: 11 16 19 F00:0816 LD HL,MaxTimeTable ; set maximum time table S06:0000237C: 21 71 23 F00:0817 LD B,4 ; preset loop count S06:0000237F: 06 04 F00:0818 ; Increment the low level base counter directly without decimal adjust F00:0819 LD A,(DE) ; load the time element S06:00002381: 1A F00:0820 INC A ; increment directly S06:00002382: 3C F00:0821 LD (DE),A ; store the time element S06:00002383: 12 F00:0822 JR UpdateTimeBuffCont1 ; skip to max limit comparison S06:00002384: 18 05 F00:0823 F00:0824 UpdateTimeBuffLoop F00:0825 ; Increment the time element decimally adjusted (only for hours, mins, secs) F00:0826 LD A,(DE) ; load the time element S06:00002386: 1A F00:0827 ADD A,1 ; increment decimally adjusted S06:00002387: C6 01 F00:0828 DAA ; S06:00002389: 27 F00:0829 LD (DE),A ; store the time element S06:0000238A: 12 F00:0830 F00:0831 UpdateTimeBuffCont1 F00:0832 ; now subtract off the limit counter, and check if hit limit (zero if hit limit) F00:0833 SUB (HL) ; subtract off the limit S06:0000238B: 96 F00:0834 RET C ; not hit limit, so exit S06:0000238C: D8 F00:0835 ; limit hit, so store the new zero value, and select the next element for processing F00:0836 LD (DE),A ; store the zero time element S06:0000238D: 12 F00:0837 INC HL ; select next maximum time value S06:0000238E: 23 F00:0838 INC DE ; select next time element S06:0000238F: 13 F00:0839 ; loop round again until all time elements exhausted (counter in B) F00:0840 DJNZ UpdateTimeBuffLoop S06:00002390: 10 F4 F00:0841 RET S06:00002392: C9 F00:0842 F00:0843 ; ------------------------------------------------------------------------------------ F00:0844 ; Interrupt driven countdown timer - update time buffer F00:0845 ; Base time element is incremented linearly F00:0846 ; All other counts (hours,mins,secs) decremented decimally F00:0847 ; All are compared to the max time table values; exit if hit limit F00:0848 F00:0849 UpdateTimeBufferDown F00:0850 ; Setup the parameters for updating the time elements, using maximum count table F00:0851 LD DE,TimeBuffBaseCount ; set base level counter S06:00002393: 11 16 19 F00:0852 LD HL,CountDownTimeTable ; set countdown time table S06:00002396: 21 75 23 F00:0853 LD B,4 ; preset loop count S06:00002399: 06 04 F00:0854 ; Increment the low level base counter directly without decimal adjust F00:0855 LD A,(DE) ; load the time element S06:0000239B: 1A F00:0856 INC A ; increment directly S06:0000239C: 3C F00:0857 LD (DE),A ; store the time element S06:0000239D: 12 F00:0858 ; now subtract off the limit counter, and check if hit limit (zero if hit limit) F00:0859 SUB (HL) ; subtract off the limit S06:0000239E: 96 F00:0860 RET C ; not hit limit, so exit S06:0000239F: D8 F00:0861 ; Continue to store the zero base value and process countdown time elements F00:0862 JR UpdateTimeBuffDownCont1 ; skip to max limit comparison S06:000023A0: 18 09 F00:0863 F00:0864 UpdateTimeBuffDownLoop F00:0865 ; Decrement the time element decimally adjusted (only for hours, mins, secs) F00:0866 LD A,(DE) ; load the time element S06:000023A2: 1A F00:0867 SUB A,1 ; decrement decimally adjusted S06:000023A3: D6 01 F00:0868 DAA ; S06:000023A5: 27 F00:0869 LD (DE),A ; store the time element S06:000023A6: 12 F00:0870 ; Check if the time element has wrapped, and if so, reload from the limit counter F00:0871 CP A,$99 ; check if it has wrapped round S06:000023A7: FE 99 F00:0872 RET NZ ; not wrapped round, so exit S06:000023A9: C0 F00:0873 ; limit hit, so reload the time element with the limit value from the tab;e F00:0874 LD A,(HL) ; reload the limited value S06:000023AA: 7E F00:0875 F00:0876 UpdateTimeBuffDownCont1 F00:0877 ; store the new limit value, and select the next element for processing F00:0878 LD (DE),A ; store the updated time element S06:000023AB: 12 F00:0879 INC HL ; select next maximum time value S06:000023AC: 23 F00:0880 INC DE ; select next time element S06:000023AD: 13 F00:0881 ; loop round again until all time elements exhausted (counter in B) F00:0882 DJNZ UpdateTimeBuffDownLoop S06:000023AE: 10 F2 F00:0883 RET S06:000023B0: C9 F00:0884 F00:0885 ; ------------------------------------------------------------------------------------ F00:0886 ; Interrupt driven clock/count down - update display buffer F00:0887 F00:0888 UpdateDisplayBuffer F00:0889 ; Convert the raw time buffer values into displayable numbers F00:0890 LD HL,DisplayBuffData ; set the display buffer start S06:000023B1: 21 10 19 F00:0891 LD DE,TimeBuffSeconds ; set the seconds time buffer S06:000023B4: 11 17 19 F00:0892 LD B,3 ; preset loop count S06:000023B7: 06 03 F00:0893 F00:0894 UpdateDispBuffLoop F00:0895 ; Convert each time element to displayable values F00:0896 LD A,(DE) ; obtain the raw value S06:000023B9: 1A F00:0897 CALL HEX7SG ; convert and store display data S06:000023BA: CD 78 06 F00:0898 INC DE ; move to next time element S06:000023BD: 13 F00:0899 DJNZ UpdateDispBuffLoop ; loop round until done S06:000023BE: 10 F9 F00:0900 ; Now step back and set decimal points between hours/misn/secs F00:0901 DEC HL ; step back to end of hours S06:000023C0: 2B F00:0902 DEC HL ; S06:000023C1: 2B F00:0903 SET 6,(HL) ; set the decimal point S06:000023C2: CB F6 F00:0904 DEC HL ; step back to end of minutes S06:000023C4: 2B F00:0905 DEC HL ; S06:000023C5: 2B F00:0906 SET 6,(HL) ; set the decimal point S06:000023C6: CB F6 F00:0907 RET S06:000023C8: C9 F00:0908 F00:0909 ; ------------------------------------------------------------------------------------ F00:0910 ; Interrupt driven clock - interrupt handling routine F00:0911 F00:0912 ClockInterrupt F00:0913 ; Preserve all registers F00:0914 PUSH AF S06:000023C9: F5 F00:0915 PUSH BC S06:000023CA: C5 F00:0916 PUSH DE S06:000023CB: D5 F00:0917 PUSH HL S06:000023CC: E5 F00:0918 ; Call the timer clock update routine, and update the display if seconds updated F00:0919 CALL UpdateTimeBufferClock ; update the time buffer for clock S06:000023CD: CD 79 23 F00:0920 LD A,B ; check returned value of counter S06:000023D0: 78 F00:0921 CP 4 ; value will be less than 4 if update made S06:000023D1: FE 04 F00:0922 CALL NZ,UpdateDisplayBuffer ; update the display if required S06:000023D3: C4 B1 23 F00:0923 ; Restore all registers, re-enable interrupts and exit F00:0924 POP HL S06:000023D6: E1 F00:0925 POP DE S06:000023D7: D1 F00:0926 POP BC S06:000023D8: C1 F00:0927 POP AF S06:000023D9: F1 F00:0928 EI S06:000023DA: FB F00:0929 RETI S06:000023DB: ED 4D F00:0930 F00:0931 ; ------------------------------------------------------------------------------------ F00:0932 ; Interrupt driven countdown timer - interrupt handling routine F00:0933 F00:0934 CountDownInterrupt F00:0935 ; Preserve all registers F00:0936 PUSH AF S06:000023DD: F5 F00:0937 PUSH BC S06:000023DE: C5 F00:0938 PUSH DE S06:000023DF: D5 F00:0939 PUSH HL S06:000023E0: E5 F00:0940 ; Call the timer countdown routine, and update the display if seconds updated F00:0941 CALL UpdateTimeBufferDown ; update the time buffer for countdown S06:000023E1: CD 93 23 F00:0942 LD A,B ; check returned value of counter S06:000023E4: 78 F00:0943 CP 4 ; value will be less than 4 if update made S06:000023E5: FE 04 F00:0944 CALL NZ,UpdateDisplayBuffer ; update the display if required S06:000023E7: C4 B1 23 F00:0945 ; Restore all registers, re-enable interrupts and exit F00:0946 POP HL S06:000023EA: E1 F00:0947 POP DE S06:000023EB: D1 F00:0948 POP BC S06:000023EC: C1 F00:0949 POP AF S06:000023ED: F1 F00:0950 EI S06:000023EE: FB F00:0951 RETI S06:000023EF: ED 4D F00:0952 F00:0953 ; ------------------------------------------------------------------------------------ F00:0954 ; interrupt driven clock/count down - clear the time buffers and updates the display F00:0955 F00:0956 ClearTimeBuffers F00:0957 ; Clear down all the time element stores, to start with a zero display F00:0958 LD HL,TimeBuffBaseCount ; set base level counter S06:000023F1: 21 16 19 F00:0959 LD B,4 ; preset loop count S06:000023F4: 06 04 F00:0960 IntClockClearLoop F00:0961 LD (HL),$00 ; zero the buffer contents S06:000023F6: 36 00 F00:0962 INC HL ; select next location S06:000023F8: 23 F00:0963 DJNZ IntClockClearLoop ; loop until done S06:000023F9: 10 FB F00:0964 ; Finally, update the display buffer from the adjusted time buffers F00:0965 CALL UpdateDisplayBuffer S06:000023FB: CD B1 23 F00:0966 RET S06:000023FE: C9 F00:0967 F00:0968 ; ------------------------------------------------------------------------------------ F00:0969 ; Interrupt driven clock/count down - shift digit into time buffer stores F00:0970 ; Shifts new hex value in bottom 4 bits of A register into the time buffer F00:0971 ; Finally, updates the display buffer from the updated time stores F00:0972 F00:0973 ClockShiftInDigit F00:0974 ; shift the time buffers left by four steps F00:0975 LD B,4 ;specify four step shift S06:000023FF: 06 04 F00:0976 ClockShiftLoop1 F00:0977 ; perform a complete left shift through all the time buffer registers F00:0978 LD HL,TimeBuffSeconds ; set the seconds time buffer S06:00002401: 21 17 19 F00:0979 SLA (HL) ; shift left one step S06:00002404: CB 26 F00:0980 INC HL ; select minutes time buffer S06:00002406: 23 F00:0981 RL (HL) ; rotete carry through one step S06:00002407: CB 16 F00:0982 INC HL ; select hours time buffer S06:00002409: 23 F00:0983 RL (HL) ; rotete carry through one step S06:0000240A: CB 16 F00:0984 ; loop round for the four step rotate F00:0985 DJNZ ClockShiftLoop1 S06:0000240C: 10 F3 F00:0986 F00:0987 ; Now patch in the new digit in bottom 4 bits of the A register to the seconds F00:0988 LD HL,TimeBuffSeconds ; set the seconds time buffer S06:0000240E: 21 17 19 F00:0989 AND $0F ; mask out top four bits S06:00002411: E6 0F F00:0990 OR (HL) ; patch in to seconds count S06:00002413: B6 F00:0991 LD (HL),A ; replace seconds count S06:00002414: 77 F00:0992 ; Finally, update the display buffer from the adjusted time buffers F00:0993 CALL UpdateDisplayBuffer S06:00002415: CD B1 23 F00:0994 RET S06:00002418: C9 F00:0995 F00:0996 F00:0997 ; ==================================================================================== F00:0998 ; Simple International telephone ring tone simulator. F00:0999 ; A really noddy demonstration copied from Experiment 16 in the MPF-1 Experiment Manual F00:1000 F00:1001 EXEC_TelRingTone F00:1002 ; Set 20Hz frequency shift rate for 1 second of ringing F00:1003 LD A,20 S06:00002419: 3E 14 F00:1004 TelRingLoop F00:1005 ; save A register and generate the two ring tones for 25ms each (20Hz rate total) F00:1006 PUSH AF S06:0000241B: F5 F00:1007 LD C,211 ; set 320Hz tone S06:0000241C: 0E D3 F00:1008 LD HL,8 ; set 25msec repeat of this tone S06:0000241E: 21 08 00 F00:1009 CALL TONE ; issue the tone S06:00002421: CD E4 05 F00:1010 LD C,140 ; set 48Hz tone S06:00002424: 0E 8C F00:1011 LD HL,12 ; set 25msec repeat of this tone S06:00002426: 21 0C 00 F00:1012 CALL TONE ; issue the tone S06:00002429: CD E4 05 F00:1013 ; restore the A register, decrement and loop round until done F00:1014 POP AF S06:0000242C: F1 F00:1015 DEC A S06:0000242D: 3D F00:1016 JR NZ,TelRingLoop S06:0000242E: 20 EB F00:1017 ; now wait for a 2 seconds silent delay F00:1018 LD BC,50000 ; set silent 2 seconds delay S06:00002430: 01 50 C3 F00:1019 CALL TelToneDelay ; wait for the delay S06:00002433: CD 76 24 F00:1020 JR EXEC_TelRingTone ; jump back to the start S06:00002436: 18 E1 F00:1021 F00:1022 ; ==================================================================================== F00:1023 ; Simple UK telephone ring tone simulator F00:1024 ; A really noddy demonstration based on Experiment 16 in the MPF-1 Experiment Manual F00:1025 F00:1026 EXEC_TelRingUKTone F00:1027 ; Set 20Hz frequency shift rate for 0.5 second of ringing F00:1028 LD A,10 S06:00002438: 3E 0A F00:1029 CALL TelRingLoop1 S06:0000243A: CD 50 24 F00:1030 ; Wait for a short 120ms delay in between F00:1031 LD BC,3000 ; set silent delay S06:0000243D: 01 B8 0B F00:1032 CALL TelToneDelay ; wait for the delay S06:00002440: CD 76 24 F00:1033 ; Set 20Hz frequency shift rate for 0.5 second of ringing F00:1034 LD A,10 S06:00002443: 3E 0A F00:1035 CALL TelRingLoop1 S06:00002445: CD 50 24 F00:1036 ; now wait for a 2 seconds silent delay F00:1037 LD BC,50000 ; set silent 2 seconds delay S06:00002448: 01 50 C3 F00:1038 CALL TelToneDelay ; wait for the delay S06:0000244B: CD 76 24 F00:1039 JR EXEC_TelRingUKTone ; jump back to the start S06:0000244E: 18 E8 F00:1040 F00:1041 TelRingLoop1 F00:1042 ; save A register and generate the two ring tones for 25ms each F00:1043 PUSH AF S06:00002450: F5 F00:1044 LD C,211 ; set 320Hz tone S06:00002451: 0E D3 F00:1045 LD HL,8 ; set 25msec repeat of this tone S06:00002453: 21 08 00 F00:1046 CALL TONE ; issue the tone S06:00002456: CD E4 05 F00:1047 LD C,140 ; set 48Hz tone S06:00002459: 0E 8C F00:1048 LD HL,12 ; set 25msec repeat of this tone S06:0000245B: 21 0C 00 F00:1049 CALL TONE ; issue the tone S06:0000245E: CD E4 05 F00:1050 ; restore the A register, decrement and loop round until done F00:1051 POP AF S06:00002461: F1 F00:1052 DEC A S06:00002462: 3D F00:1053 JR NZ,TelRingLoop1 S06:00002463: 20 EB F00:1054 RET S06:00002465: C9 F00:1055 F00:1056 ; ==================================================================================== F00:1057 ; Simple Busy telephone ring tone simulator F00:1058 ; A really noddy demonstration based on Experiment 16 in the MPF-1 Experiment Manual F00:1059 F00:1060 EXEC_TelRingBusyTone F00:1061 ; generate the single busy tone for 500ms period F00:1062 LD C,169 ; set 400Hz tone S06:00002466: 0E A9 F00:1063 LD HL,200 ; set 0.5 repeat of this tone S06:00002468: 21 C8 00 F00:1064 CALL TONE ; issue the tone S06:0000246B: CD E4 05 F00:1065 ; now wait for a 0.5 seconds silent delay F00:1066 LD BC,12500 ; set silent 0.5 seconds delay S06:0000246E: 01 D4 30 F00:1067 CALL TelToneDelay ; wait for the delay S06:00002471: CD 76 24 F00:1068 JR EXEC_TelRingBusyTone ; jump back to the start S06:00002474: 18 F0 F00:1069 F00:1070 ; ------------------------------------------------------------------------------------ F00:1071 ; Special 40us delay routine for the silent pauses produced by the tone generator F00:1072 TelToneDelay F00:1073 EX (SP),HL ; 19 states used S06:00002476: E3 F00:1074 EX (SP),HL ; 19 states used S06:00002477: E3 F00:1075 CPI ; decrement counter, 16 states used S06:00002478: ED A1 F00:1076 RET PO ; return once hit zero, 5 states used S06:0000247A: E0 F00:1077 JR TelToneDelay ; loop round, 12 states used S06:0000247B: 18 F9 F00:1078 F00:1079 ; ==================================================================================== F00:1080 ; Simple electronic organ, which also briefly displays the key code. F00:1081 ; Another fairly noddy demonstration program copied from Experiment 17 in the MPF-1 F00:1082 ; Experiment Manual. Note that all 16 keys on the main keyboard are implemented F00:1083 ; (only the far left hand column keys do not generate tones). F00:1084 F00:1085 EXEC_ElectronicOrgan F00:1086 ; First clear down the entire display buffer, ready for updates F00:1087 CALL ClearDispBuffer S06:0000247D: CD F9 21 F00:1088 ; Blank the display and wait until the key is pressed F00:1089 LD IX,DisplayBuffData ; set scan buffer start S06:00002480: DD 21 10 19 F00:1090 CALL SCAN ; wait for key pressed S06:00002484: CD FE 05 F00:1091 ; Display the pressed key code, preserving the key in the A register F00:1092 PUSH AF ; preserve the pressed key S06:00002487: F5 F00:1093 LD HL,DisplayBuffData ; set start of data field S06:00002488: 21 10 19 F00:1094 CALL HEX7SG ; display data from A register S06:0000248B: CD 78 06 F00:1095 LD IX,DisplayBuffData ; set scan buffer start S06:0000248E: DD 21 10 19 F00:1096 CALL SCAN1 ; update the display without waiting S06:00002492: CD 24 06 F00:1097 POP AF ; restore the pressed key S06:00002495: F1 F00:1098 ; Use the pressed key code to generate an offset into the keyboard code table F00:1099 LD HL,OrganFreqTable ; obtain starting address of table S06:00002496: 21 B4 24 F00:1100 LD D,0 ; transfer key code to offset in DE S06:00002499: 16 00 F00:1101 LD E,A ; S06:0000249B: 5F F00:1102 ADD HL,DE ; add the key code to this address S06:0000249C: 19 F00:1103 ; Setup the initial raw value to be poutput to the speaker and keyboard control F00:1104 ; Bit 7 controls the speaker, while bits 0 to 5 control the keyboard scan F00:1105 LD A,$C0 ; speaker on bit 7, enable scan S06:0000249D: 3E C0 F00:1106 F00:1107 OrganHalfPeriod F00:1108 ; Output the current A value to the speaker and obtain frequency from table F00:1109 OUT (DIGIT),A ; output raw value S06:0000249F: D3 02 F00:1110 LD B,(HL) ; obtain frequency S06:000024A1: 46 F00:1111 F00:1112 OrganDelayLoop F00:1113 ; Execute simple delay for the number of cycles specified in B F00:1114 NOP S06:000024A2: 00 F00:1115 NOP S06:000024A3: 00 F00:1116 NOP S06:000024A4: 00 F00:1117 DJNZ OrganDelayLoop S06:000024A5: 10 FB F00:1118 ; Complement the speaker output on bit 7 in A register F00:1119 XOR $80 ; toggle speaker output S06:000024A7: EE 80 F00:1120 LD C,A ; preserve A in C register S06:000024A9: 4F F00:1121 ; Read in the raw keyboard input, masking out tape input and user key F00:1122 IN A,(KIN) ; read raw keyboard input S06:000024AA: DB 00 F00:1123 OR $C0 ; mask out bits 6 and 7 S06:000024AC: F6 C0 F00:1124 ; Detect if all keys are now released, restarting completely if so F00:1125 INC A ; detect if all bits are set (Z will be set) S06:000024AE: 3C F00:1126 LD A,C ; restore A register for control S06:000024AF: 79 F00:1127 JR Z,EXEC_ElectronicOrgan ; restart if all keys released S06:000024B0: 28 CB F00:1128 ; Key not released yet, so loop round, toggling the speaker output each time F00:1129 JR OrganHalfPeriod ; loop round to generate tone S06:000024B2: 18 EB F00:1130 F00:1131 ; ------------------------------------------------------------------------------------ F00:1132 ; Frequency table for simple electronic organ F00:1133 OrganFreqTable F00:1134 DB $B2 ; key 0 (B) S06:000024B4: B2 F00:1135 DB $A8 ; key 1 (C) S06:000024B5: A8 F00:1136 DB $96 ; key 2 (D) S06:000024B6: 96 F00:1137 DB $85 ; key 3 (E) S06:000024B7: 85 F00:1138 DB $7E ; key 4 (F) S06:000024B8: 7E F00:1139 DB $70 ; key 5 (G) S06:000024B9: 70 F00:1140 DB $64 ; key 6 (A) S06:000024BA: 64 F00:1141 DB $59 ; key 7 (B) S06:000024BB: 59 F00:1142 DB $54 ; key 8 (C) S06:000024BC: 54 F00:1143 DB $4A ; key 9 (D) S06:000024BD: 4A F00:1144 DB $42 ; key A (E) S06:000024BE: 42 F00:1145 DB $3E ; key B (F) S06:000024BF: 3E F00:1146 DB $37 ; key C (G) S06:000024C0: 37 F00:1147 DB $31 ; key D (A) S06:000024C1: 31 F00:1148 DB $2C ; key E (B) S06:000024C2: 2C F00:1149 DB $29 ; key F (C) S06:000024C3: 29 F00:1150 ; The remaining keys are mostly semi-tones and are mapped rather strangely F00:1151 DB $D4 ; key + (#G) S06:000024C4: D4 F00:1152 DB $77 ; key - (#F) S06:000024C5: 77 F00:1153 DB $C8 ; key GO (A) S06:000024C6: C8 F00:1154 DB $8D ; key STEP (#D) S06:000024C7: 8D F00:1155 DB $6A ; key DATA (#G) S06:000024C8: 6A F00:1156 DB $27 ; key SBR (#C) S06:000024C9: 27 F00:1157 DB $2E ; key INS (#A) S06:000024CA: 2E F00:1158 DB $4F ; key DEL (#C) S06:000024CB: 4F F00:1159 DB $25 ; key PC (D) S06:000024CC: 25 F00:1160 DB $BD ; key ADDR (#A) S06:000024CD: BD F00:1161 DB $46 ; key CBR (#D) S06:000024CE: 46 F00:1162 DB $3B ; key REG (#F) S06:000024CF: 3B F00:1163 DB $34 ; key MOVE (#G) S06:000024D0: 34 F00:1164 DB $5E ; key RELA (#A) S06:000024D1: 5E F00:1165 DB $9F ; key TWR (#C) S06:000024D2: 9F F00:1166 DB $E1 ; key TRD (G) S06:000024D3: E1 F00:1167 F00:1168 ; ==================================================================================== F00:1169 ; Simple electronic music box, playing two different tunes. F00:1170 ; This is copied from Experiment 18 in the MPF-1 Experiment Manual F00:1171 F00:1172 EXEC_MusicBoxJingleBells F00:1173 LD IY,SongTableJingleBells S06:000024D4: FD 21 5D 25 F00:1174 JR MusicBoxCommonStart S06:000024D8: 18 06 F00:1175 F00:1176 ; .................................................................................... F00:1177 EXEC_MusicBoxGreenSleeves F00:1178 LD IY,SongTableGreenSleeves S06:000024DA: FD 21 7D 25 F00:1179 JR MusicBoxCommonStart S06:000024DE: 18 00 F00:1180 F00:1181 ; .................................................................................... F00:1182 MusicBoxCommonStart F00:1183 PUSH IY ; transfer music table start in IY S06:000024E0: FD E5 F00:1184 POP IX ; to IX register ready for use S06:000024E2: DD E1 F00:1185 F00:1186 MusicBoxMainLoop F00:1187 ; obtain note data, and check higher level control flags F00:1188 LD A,(IX) ; obtain note data from note table S06:000024E4: DD 7E 00 F00:1189 ADD A,A ; multiply by two and set flags S06:000024E7: 87 F00:1190 JR C,MusicBoxHalt ; halt if bit 7 originally set S06:000024E8: 38 32 F00:1191 JP M,MusicBoxCommonStart ; repeat if bit 6 originally set S06:000024EA: FA E0 24 F00:1192 ; now check whether or not this is a rest period F00:1193 LD C,0 ; preset to rest mode S06:000024ED: 0E 00 F00:1194 BIT 6,A ; check if rest bit set S06:000024EF: CB 77 F00:1195 JR NZ,MusicBoxPlayNote ; skip next bit if so S06:000024F1: 20 02 F00:1196 SET 7,C ; set to normal note out mode S06:000024F3: CB F9 F00:1197 F00:1198 MusicBoxPlayNote F00:1199 ; look up the note in the frequency table F00:1200 AND $3F ; mask out the note data (bit 0 always clear) S06:000024F5: E6 3F F00:1201 LD HL,MusicBoxFreqTable ; obtain frequency table pointer S06:000024F7: 21 1D 25 F00:1202 LD D,0 ; transfer note data to offset in DE S06:000024FA: 16 00 F00:1203 LD E,A ; S06:000024FC: 5F F00:1204 ADD HL,DE ; add the key code to this address S06:000024FD: 19 F00:1205 ; load in the data from the frequency table, and the next count byte from the note table F00:1206 LD E,(HL) ; counts of loop per half-period delay S06:000024FE: 5E F00:1207 INC HL ; move on to next freq table byte S06:000024FF: 23 F00:1208 LD D,(HL) ; counts of half-periods per unit-time S06:00002500: 56 F00:1209 INC IX ; move to next byte in note table S06:00002501: DD 23 F00:1210 LD H,(IX) ; counts of unit-time for this note S06:00002503: DD 66 00 F00:1211 LD A,$FF ; preset the data to be output to the speaker S06:00002506: 3E FF F00:1212 F00:1213 MusicBoxGenTone F00:1214 ; generate the tone or rest period, running for specified delay F00:1215 LD L,D ; set half-period count S06:00002508: 6A F00:1216 MusicBoxUnit F00:1217 OUT (DIGIT),A ; Bit 7 is NOTE-OUT S06:00002509: D3 02 F00:1218 LD B,E ; set loop counter S06:0000250B: 43 F00:1219 MusicBoxDelay F00:1220 NOP ; wait for preset delay S06:0000250C: 00 F00:1221 NOP S06:0000250D: 00 F00:1222 NOP S06:0000250E: 00 F00:1223 DJNZ MusicBoxDelay ; loop round until done S06:0000250F: 10 FB F00:1224 ; now toggle the speaker data top bit if NOT a rest period; this generates the tone F00:1225 XOR C ; toggle for tone out, not for rest S06:00002511: A9 F00:1226 ; now decrement the half period counter in L and loop round until done F00:1227 DEC L S06:00002512: 2D F00:1228 JR NZ,MusicBoxUnit S06:00002513: 20 F4 F00:1229 ; now decrement the unit time counter in H and loop round until done F00:1230 DEC H S06:00002515: 25 F00:1231 JR NZ,MusicBoxGenTone S06:00002516: 20 F0 F00:1232 ; current note/rest has ended, so increment pointer ad loop round F00:1233 INC IX S06:00002518: DD 23 F00:1234 JR MusicBoxMainLoop S06:0000251A: 18 C8 F00:1235 F00:1236 MusicBoxHalt F00:1237 ; halt execution of music box F00:1238 HALT S06:0000251C: 76 F00:1239 F00:1240 ; ------------------------------------------------------------------------------------ F00:1241 ; Frequency/timing table for the music box F00:1242 ; Low order byte is the counts of delay loop per half-period (matches organ tones) F00:1243 ; High order byte is counts of half-period per unit time F00:1244 F00:1245 MusicBoxFreqTable F00:1246 ; Octave 3 F00:1247 DW $18E1 ; code 00 (G) S06:0000251D: E1 18 F00:1248 DW $1AD4 ; code 01 (#G) S06:0000251F: D4 1A F00:1249 DW $1BC8 ; code 02 (A) S06:00002521: C8 1B F00:1250 DW $1DBD ; code 03 (#A) S06:00002523: BD 1D F00:1251 DW $1EB2 ; code 04 (B) S06:00002525: B2 1E F00:1252 ; Octave 4 F00:1253 DW $20A8 ; code 05 (C) S06:00002527: A8 20 F00:1254 DW $229F ; code 06 (#C) S06:00002529: 9F 22 F00:1255 DW $2496 ; code 07 (D) S06:0000252B: 96 24 F00:1256 DW $268D ; code 08 (#D) S06:0000252D: 8D 26 F00:1257 DW $2985 ; code 09 (E) S06:0000252F: 85 29 F00:1258 DW $2B7E ; code 0A (F) S06:00002531: 7E 2B F00:1259 DW $2E77 ; code 0B (#F) S06:00002533: 77 2E F00:1260 DW $3170 ; code 0C (G) S06:00002535: 70 31 F00:1261 DW $336A ; code 0D (#G) S06:00002537: 6A 33 F00:1262 DW $3764 ; code 0E (A) S06:00002539: 64 37 F00:1263 DW $3A5E ; code 0F (#A) S06:0000253B: 5E 3A F00:1264 DW $3D59 ; code 10 (B) S06:0000253D: 59 3D F00:1265 ; Octave 5 F00:1266 DW $4154 ; code 11 (C) S06:0000253F: 54 41 F00:1267 DW $454F ; code 12 (#C) S06:00002541: 4F 45 F00:1268 DW $494A ; code 13 (D) S06:00002543: 4A 49 F00:1269 DW $AD46 ; code 14 (#D) S06:00002545: 46 AD F00:1270 DW $5242 ; code 15 (E) S06:00002547: 42 52 F00:1271 DW $573E ; code 16 (F) S06:00002549: 3E 57 F00:1272 DW $5C3B ; code 17 (#F) S06:0000254B: 3B 5C F00:1273 DW $6237 ; code 18 (G) S06:0000254D: 37 62 F00:1274 DW $6734 ; code 19 (#G) S06:0000254F: 34 67 F00:1275 DW $6E31 ; code 1A (A) S06:00002551: 31 6E F00:1276 DW $742E ; code 1B (#A) S06:00002553: 2E 74 F00:1277 DW $7B2C ; code 1C (B) S06:00002555: 2C 7B F00:1278 ; Octave 6 F00:1279 DW $8229 ; code 1D (C) S06:00002557: 29 82 F00:1280 DW $8A27 ; code 1E (#C) S06:00002559: 27 8A F00:1281 DW $9225 ; code 1F (D) S06:0000255B: 25 92 F00:1282 F00:1283 F00:1284 ; ------------------------------------------------------------------------------------ F00:1285 ; Jingle Bells song table F00:1286 ; Low order byte encodes bit 7, 6, 5& 4 - 0 as stop, repeat, rest and note, respectively F00:1287 ; High order byte is not length in unit time counts (n * 0.077 sec) F00:1288 F00:1289 SongTableJingleBells F00:1290 DW $0409 S06:0000255D: 09 04 F00:1291 DW $0409 S06:0000255F: 09 04 F00:1292 DW $0609 S06:00002561: 09 06 F00:1293 DW $0220 S06:00002563: 20 02 F00:1294 DW $0409 S06:00002565: 09 04 F00:1295 DW $0409 S06:00002567: 09 04 F00:1296 DW $0609 S06:00002569: 09 06 F00:1297 DW $0220 S06:0000256B: 20 02 F00:1298 DW $0409 S06:0000256D: 09 04 F00:1299 DW $040C S06:0000256F: 0C 04 F00:1300 DW $0405 S06:00002571: 05 04 F00:1301 DW $0407 S06:00002573: 07 04 F00:1302 DW $0809 S06:00002575: 09 08 F00:1303 DW $0820 S06:00002577: 20 08 F00:1304 DW $2020 ; long pause S06:00002579: 20 20 F00:1305 DW $0040 ; repeat the song S06:0000257B: 40 00 F00:1306 F00:1307 F00:1308 ; ------------------------------------------------------------------------------------ F00:1309 ; Green Sleeves song table F00:1310 SongTableGreenSleeves F00:1311 DW $0807 S06:0000257D: 07 08 F00:1312 DW $100A S06:0000257F: 0A 10 F00:1313 DW $080C S06:00002581: 0C 08 F00:1314 DW $100E S06:00002583: 0E 10 F00:1315 DW $0410 S06:00002585: 10 04 F00:1316 DW $040E S06:00002587: 0E 04 F00:1317 DW $100C S06:00002589: 0C 10 F00:1318 DW $0809 S06:0000258B: 09 08 F00:1319 F00:1320 DW $1005 S06:0000258D: 05 10 F00:1321 DW $0407 S06:0000258F: 07 04 F00:1322 DW $0409 S06:00002591: 09 04 F00:1323 DW $100A S06:00002593: 0A 10 F00:1324 DW $0807 S06:00002595: 07 08 F00:1325 DW $1007 S06:00002597: 07 10 F00:1326 DW $0406 S06:00002599: 06 04 F00:1327 DW $0407 S06:0000259B: 07 04 F00:1328 F00:1329 DW $1009 S06:0000259D: 09 10 F00:1330 DW $0806 S06:0000259F: 06 08 F00:1331 DW $1002 S06:000025A1: 02 10 F00:1332 DW $0807 S06:000025A3: 07 08 F00:1333 DW $100A S06:000025A5: 0A 10 F00:1334 DW $080C S06:000025A7: 0C 08 F00:1335 DW $100E S06:000025A9: 0E 10 F00:1336 DW $0410 S06:000025AB: 10 04 F00:1337 F00:1338 DW $040E S06:000025AD: 0E 04 F00:1339 DW $100C S06:000025AF: 0C 10 F00:1340 DW $0809 S06:000025B1: 09 08 F00:1341 DW $1005 S06:000025B3: 05 10 F00:1342 DW $0407 S06:000025B5: 07 04 F00:1343 DW $0409 S06:000025B7: 09 04 F00:1344 DW $080A S06:000025B9: 0A 08 F00:1345 DW $0809 S06:000025BB: 09 08 F00:1346 F00:1347 DW $0807 S06:000025BD: 07 08 F00:1348 DW $0806 S06:000025BF: 06 08 F00:1349 DW $0804 S06:000025C1: 04 08 F00:1350 DW $0806 S06:000025C3: 06 08 F00:1351 DW $1007 S06:000025C5: 07 10 F00:1352 DW $0820 S06:000025C7: 20 08 F00:1353 DW $1011 S06:000025C9: 11 10 F00:1354 DW $0811 S06:000025CB: 11 08 F00:1355 F00:1356 DW $1011 S06:000025CD: 11 10 F00:1357 DW $0410 S06:000025CF: 10 04 F00:1358 DW $040E S06:000025D1: 0E 04 F00:1359 DW $100C S06:000025D3: 0C 10 F00:1360 DW $0809 S06:000025D5: 09 08 F00:1361 DW $1005 S06:000025D7: 05 10 F00:1362 DW $0407 S06:000025D9: 07 04 F00:1363 DW $0409 S06:000025DB: 09 04 F00:1364 F00:1365 DW $100A S06:000025DD: 0A 10 F00:1366 DW $0807 S06:000025DF: 07 08 F00:1367 DW $1007 S06:000025E1: 07 10 F00:1368 DW $0406 S06:000025E3: 06 04 F00:1369 DW $0407 S06:000025E5: 07 04 F00:1370 DW $1009 S06:000025E7: 09 10 F00:1371 DW $0806 S06:000025E9: 06 08 F00:1372 DW $1002 S06:000025EB: 02 10 F00:1373 F00:1374 DW $0820 S06:000025ED: 20 08 F00:1375 DW $1011 S06:000025EF: 11 10 F00:1376 DW $0811 S06:000025F1: 11 08 F00:1377 DW $1011 S06:000025F3: 11 10 F00:1378 DW $0410 S06:000025F5: 10 04 F00:1379 DW $040E S06:000025F7: 0E 04 F00:1380 DW $100C S06:000025F9: 0C 10 F00:1381 DW $0809 S06:000025FB: 09 08 F00:1382 F00:1383 DW $1005 S06:000025FD: 05 10 F00:1384 DW $0407 S06:000025FF: 07 04 F00:1385 DW $0409 S06:00002601: 09 04 F00:1386 DW $080A S06:00002603: 0A 08 F00:1387 DW $0809 S06:00002605: 09 08 F00:1388 DW $0807 S06:00002607: 07 08 F00:1389 DW $0806 S06:00002609: 06 08 F00:1390 DW $0804 S06:0000260B: 04 08 F00:1391 F00:1392 DW $0806 S06:0000260D: 06 08 F00:1393 DW $1807 S06:0000260F: 07 18 F00:1394 DW $1020 S06:00002611: 20 10 F00:1395 DW $0040 S06:00002613: 40 00 F00:1396 F00:1397 F00:1398 ; ==================================================================================== F00:1399 ; ==================================================================================== F00:1400 Sections: S01 seg1910 S02 seg2000 S03 seg2080 S04 seg20e0 S05 seg20f0 S06 seg2100 Sources: F00 mpf_utils_rac.z80 Symbols: MusicBoxDelay LAB (0x250c) sec=seg2100 MusicBoxUnit LAB (0x2509) sec=seg2100 MusicBoxGenTone LAB (0x2508) sec=seg2100 MusicBoxFreqTable LAB (0x251d) sec=seg2100 MusicBoxPlayNote LAB (0x24f5) sec=seg2100 MusicBoxHalt LAB (0x251c) sec=seg2100 MusicBoxMainLoop LAB (0x24e4) sec=seg2100 SongTableGreenSleeves LAB (0x257d) sec=seg2100 MusicBoxCommonStart LAB (0x24e0) sec=seg2100 SongTableJingleBells LAB (0x255d) sec=seg2100 OrganDelayLoop LAB (0x24a2) sec=seg2100 OrganHalfPeriod LAB (0x249f) sec=seg2100 OrganFreqTable LAB (0x24b4) sec=seg2100 TelRingLoop1 LAB (0x2450) sec=seg2100 TelToneDelay LAB (0x2476) sec=seg2100 TelRingLoop LAB (0x241b) sec=seg2100 ClockShiftLoop1 LAB (0x2401) sec=seg2100 IntClockClearLoop LAB (0x23f6) sec=seg2100 UpdateDispBuffLoop LAB (0x23b9) sec=seg2100 UpdateDisplayBuffer LAB (0x23b1) sec=seg2100 UpdateTimeBuffDownLoop LAB (0x23a2) sec=seg2100 UpdateTimeBuffDownCont1 LAB (0x23ab) sec=seg2100 UpdateTimeBufferDown LAB (0x2393) sec=seg2100 UpdateTimeBuffLoop LAB (0x2386) sec=seg2100 UpdateTimeBuffCont1 LAB (0x238b) sec=seg2100 UpdateTimeBufferClock LAB (0x2379) sec=seg2100 CountDownTimeTable LAB (0x2375) sec=seg2100 MaxTimeTable LAB (0x2371) sec=seg2100 IntCountDownCheckZero LAB (0x2366) sec=seg2100 IntCountDownMainLoop LAB (0x235a) sec=seg2100 IntDrivenCountDownRun LAB (0x2341) sec=seg2100 IntCountDownSetLoop LAB (0x232f) sec=seg2100 IntClockMainLoop LAB (0x2323) sec=seg2100 ClockShiftInDigit LAB (0x23ff) sec=seg2100 IntDrivenClockRun LAB (0x230a) sec=seg2100 IntClockSetLoop LAB (0x22f8) sec=seg2100 ClearTimeBuffers LAB (0x23f1) sec=seg2100 TestEachSegInnderLoop LAB (0x22de) sec=seg2100 TestEachSegOuterLoop LAB (0x22d2) sec=seg2100 DispLampTestEnd LAB (0x22c3) sec=seg2100 DispLampTestDpLoop LAB (0x22be) sec=seg2100 KeyCodePosWaitLoop LAB (0x22a0) sec=seg2100 KeyCodePosCkeckKey LAB (0x2287) sec=seg2100 KeyCodePosMainLoop LAB (0x2278) sec=seg2100 KeyCodeMainLoop LAB (0x2242) sec=seg2100 WaitDelayScanInnerLoop LAB (0x2212) sec=seg2100 WaitDelayInnerLoop LAB (0x2207) sec=seg2100 WaitLongDelay LAB (0x2204) sec=seg2100 DispBuffClrLoop LAB (0x21fe) sec=seg2100 WaitLongDelayScan LAB (0x220f) sec=seg2100 RamTestFailDispLoop LAB (0x21d6) sec=seg2100 PassDisplayData LAB (0x21bd) sec=seg2100 RamPattTestFail LAB (0x21ae) sec=seg2100 RamPattTestLoop LAB (0x21a1) sec=seg2100 RamTestFailDisplay LAB (0x21c3) sec=seg2100 RamTestPassDisplay LAB (0x21b3) sec=seg2100 RamAddressReadLoop LAB (0x217c) sec=seg2100 RamAddrTestFail LAB (0x2194) sec=seg2100 RamAddrWriteFinished LAB (0x216e) sec=seg2100 RamAddressWriteLoop LAB (0x215a) sec=seg2100 RomCheckEnd LAB (0x2143) sec=seg2100 RomCheckLoop LAB (0x2129) sec=seg2100 ClearDispBuffer LAB (0x21f9) sec=seg2100 RomCheckCommon LAB (0x2121) sec=seg2100 CountDownInterrupt LAB (0x23dd) sec=seg2100 ClockInterrupt LAB (0x23c9) sec=seg2100 EXEC_MusicBoxGreenSleeves LAB (0x24da) sec=seg2100 CALL_MusicBoxGreenSleeves LAB (0x20c4) sec=seg2080 EXEC_MusicBoxJingleBells LAB (0x24d4) sec=seg2100 CALL_MusicBoxJingleBells LAB (0x20c0) sec=seg2080 EXEC_ElectronicOrgan LAB (0x247d) sec=seg2100 CALL_ElectronicOrgan LAB (0x20bc) sec=seg2080 EXEC_TelRingBusyTone LAB (0x2466) sec=seg2100 CALL_TelRingBusyTone LAB (0x20b8) sec=seg2080 EXEC_TelRingUKTone LAB (0x2438) sec=seg2100 CALL_TelRingUKTone LAB (0x20b4) sec=seg2080 EXEC_TelRingTone LAB (0x2419) sec=seg2100 CALL_TelRingTone LAB (0x20b0) sec=seg2080 EXEC_IntDrivenCountDown LAB (0x232c) sec=seg2100 CALL_IntDrivenCountDown LAB (0x20ac) sec=seg2080 EXEC_IntDrivenClock LAB (0x22f5) sec=seg2100 CALL_IntDrivenClock LAB (0x20a8) sec=seg2080 EXEC_TestEachDisplaySegment LAB (0x22cd) sec=seg2100 CALL_TestEachDisplaySegment LAB (0x20a4) sec=seg2080 EXEC_TestAllDisplaySegments LAB (0x22a7) sec=seg2100 CALL_TestAllDisplaySegments LAB (0x20a0) sec=seg2080 EXEC_KeyCodeDisplayPos LAB (0x225b) sec=seg2100 CALL_KeyCodeDisplayPos LAB (0x209c) sec=seg2080 EXEC_KeyCodeDisplay LAB (0x2225) sec=seg2100 CALL_KeyCodeDisplay LAB (0x2098) sec=seg2080 EXEC_RamPatternTest LAB (0x2198) sec=seg2100 CALL_RamPatternTest LAB (0x2094) sec=seg2080 EXEC_RamAddressTest LAB (0x214d) sec=seg2100 CALL_RamAddressTest LAB (0x2090) sec=seg2080 EXEC_ExpansionRomChecksum LAB (0x211b) sec=seg2100 CALL_ExpansionRomChecksum LAB (0x208c) sec=seg2080 EXEC_FullMonitorRomChecksum LAB (0x2112) sec=seg2100 CALL_FullMonitorRomChecksum LAB (0x2088) sec=seg2080 EXEC_TopMonitorRomChecksum LAB (0x2109) sec=seg2100 CALL_TopMonitorRomChecksum LAB (0x2084) sec=seg2080 EXEC_BaseMonitorRomChecksum LAB (0x2100) sec=seg2100 CALL_BaseMonitorRomChecksum LAB (0x2080) sec=seg2080 TimeBuffHours LAB (0x1919) sec=seg1910 TimeBuffMinutes LAB (0x1918) sec=seg1910 TimeBuffSeconds LAB (0x1917) sec=seg1910 TimeBuffBaseCount LAB (0x1916) sec=seg1910 DisplayBuffAddr LAB (0x1912) sec=seg1910 DisplayBuffData LAB (0x1910) sec=seg1910 BEEP_1K_DURATION EXPR(32) DIGIT_SCAN_HL_DELAY EXPR(1) FAIL_MSG_HL_DELAY EXPR(3) SCAN_BASE_DELAY_VAL EXPR(32) MAX_BASE_DELAY_VAL EXPR(65535) DispBuffFullLen EXPR(6) DispBuffAddrLen EXPR(4) DispBuffDataLen EXPR(2) RAM_MEM_SIZE EXPR(2048) RAM_MEM_START EXPR(6144) MON_ISR_SVC_ADDR EXPR(8190) EXP_ROM_RAM_VAR_START EXPR(6416) EXP_ROM_INT_VECT2_BASE EXPR(240) EXP_ROM_INT_VECT2 EXPR(8432) EXP_ROM_INT_VECT1_BASE EXPR(224) EXP_ROM_INT_VECT1 EXPR(8416) EXP_ROM_CODE_START EXPR(8448) EXP_ROM_JUMP_TAB EXPR(8320) EXP_ROM_BASE_SIZE EXPR(2048) EXP_ROM_START EXPR(8192) MON_ROM_FULL_SIZE EXPR(4096) MON_ROM_TOP_SIZE EXPR(2048) MON_ROM_TOP_START EXPR(2048) MON_ROM_BASE_SIZE EXPR(2048) MON_ROM_BASE_START EXPR(0) KIN EXPR(0) DIGIT EXPR(2) CTC0 EXPR(64) TONE1K EXPR(1502) RAMCHK EXPR(1526) BLANK EXPR(1957) TONE EXPR(1508) SCAN1 EXPR(1572) SCAN EXPR(1534) HEX7SG EXPR(1656) __VASM EXPR(0) INTERNAL There have been no errors.