|
Quite some time ago, in October 2011, I built a simple Z80 based computer with an IDE interface, embedded Forth interpreter etc. This machine is described in more detail here. Since the overall system occupies a 10 inch enclosure and is completely wire-wrapped it is not as portable as I wished it was. Thus I decided to reimplement it and design a real printed circuit board using EAGLE - the free version of this CAD system allows one to design boards of up to 100 mm times 80 mm in size which is exactly the size I envisioned for this new Z80 system. This small size required some sacrifices - most notably there is longer an IDE-subsystem and the only IO-port is a serial line with which the Z80 board can be connected to a terminal or even better a host computer. This small computer is extremely simple to build, the printed circuit boards can be manufactured based on the EAGLE files enclosed below and using the builtin Forth interpreter is really fun. |
|
![]() |
The overall system is shown on the left. From left to right the bottom row of ICs contains a 16C550 UART (serial line interface), a Z80B processor running at a whopping 6 MHz, 32 kB of RAM (62256) and 32 kB EPROM (27C512) containing the monitor and the Forth interpreter. Above the RAM and ROM are the two TTL quartz oscillators for the UART (1.8432 MHz) and the CPU (6 MHz) and, on the far right, a 74LS32 for address decoding. The remaining parts are a MAX232 line driver for the serial line (next to the six small electrolytic capacitors) and a 74HCT14 which is mainly used for the reset circuitry (any 74xx14 should do fine here). On the upper left, the power supply connector can be seen (a simple floppy disk like jack) with the tiny yellow reset switch just below. The ribbon cable connected on the upper right is the serial line. |
The schematic of this board is shown on the right (click on the image to get a larger and readable version). In the center, the Z80 CPU can be seen. The reset circuit, which consists of an 10 uF electrolytic capacitor which is loaded via a 10k resistor and feeds two Schmitt trigger inverter gates of the 74HCT14, can be seen on the left of the drawing. When the reset switch is depressed, the capacitor is discharged and a suitable reset pulse is supplied to the CPU (active low) and the UART (active high). The 16 bit address space of the CPU is divided into two 32 kB areas by means of A15. The lower 32 kB from $0000 to $7FFF are occupied by ROM (27C512 EPROM) while the upper 32 kB from $8000 to $FFFF are RAM area. The UART 16C550 with its associated level converter MAX232 can be seen in the lower right corner of the schematic. |
![]() |
![]() |
Based on this schematic a printed circuit board was created using EAGLE's autorouter (as you can see I was lazy and did not take any special care of power supply lines since there are so few components which need no special precautions). There are many companies which can create a board based on the Z80_mini.brd file which can be found in this ZIP-file. My board was made by Jackaltac and I am very satisfied by the result. (No, I am not affiliated with this company nor do I intend to make any advertising - it is just that I have made some good experiences with their services.) The overall system requires a 5 V power supply at about 120 mA with the parts specified above and running at 6 MHz. |
A computer without software is useless, so I needed at least a simple EPROM based monitor for this computer to work with it at all. This monitor is based on the monitor of the predecessor Z80-system, the Tiny Z80 system mentioned above. Writing a monitor turned out to be really fun and what once started with the goal of something capable of loading and running programs from Hex files became a monitor with embedded Forth interpreter (I ported Brad Rodriguez' CAMEL Forth) and many other useful features. (The monitor intended for the Tiny Z80 system contains routines for an IDE interface - these routines are also included in the monitor for the Z80_mini described here, but can not be used due to the missing hardware!) I use the zasm assembler to assembly the monitor sources and produce an Intel-Hex file suitable for an EPROM programmer. The complete sources of the monitor can be found here. This ZIP-archive contains a README.txt file with additional information on how to build a monitor EPROM image file. If you just want to program a 27C512 EPROM with version 0.14a of the monitor (which is the latest version as of 28-JUN-2013) you can download a suitable Intel-Hex file here. (If you have no EPROM programmer you can send me a mail - see below - and ask me to program it for you given that you send me an empty 27(C)512 EPROM and the necessary postage and packaging to return it to you.) On power on, the monitor prints this welcome-message: Simple Z80-monitor - V 0.14a (B. Ulmann, September 2011 - June 2013) This monitor contains Brad Rodriguez' CAMEL Forth, John Kerr's Z80 disassembler ------------------------------------------------------------------------------- Z>The command "language" is quite simple: All commands are grouped in so-called "command groups". Each such group is selected by a single letter and contains commands which are select by a second single letter. So there is no need to press "ENTER" as in conventional shells or type long commands. If a command requires parameters it prompts the user to enter all required values. There is one command group containing only one command, so that a single letter instead of two as written above is sufficient to execute this command which is the HELP-command: Z> HELP: Known command groups and commands: C(ontrol group): C(old start), I(nfo), S(tart), W(arm start) D(isk group): I(nfo), M(ount), T(ransfer), U(nmount) R(ead), W(rite) F(ile group): C(at), D(irectory), L(oad), R(un) H(elp) M(emory group): (dis)A(ssemble), D(ump), E(xamine), F(ill), I(ntel Hex Load), L(oad), R(egister dump), S(ubsystem group): F(orth)It should be noted again that all commands regarding disk and file IO will not work and will most likely crash the system since there is no IDE controller. I was too lazy to surgically remove all of the IDE support which is quite scattered within the monitor. :-) The following example shows the use of a command which requires a start and an end address to work - a disassembler listing covering the memory area from $0000 to $0050 is generated (the disassembler has been developed by John Kerr): Z> MEMORY/DISASSEMBLE: START=0000 END=0050 0000 F3 DI 0001 31 3B FB LD SP,0FB3BH 0004 18 17 JR 1DH 0006 00 NOP 0007 00 NOP 0008 C5 PUSH BC 0009 E5 PUSH HL 000A DD E5 PUSH IX 000C E1 POP HL 000D 29 ADD HL,HL 000E 01 4C 1D LD BC,1D4CH 0011 09 ADD HL,BC 0012 4E LD C,(HL) 0013 23 INC HL 0014 46 LD B,(HL) 0015 2B DEC HL 0016 C5 PUSH BC 0017 DD E1 POP IX 0019 E1 POP HL 001A C1 POP BC 001B DD E9 JP (IX) 001D 3E 80 LD A,80H 001F D3 03 OUT (3),A 0021 3E 0C LD A,0CH 0023 D3 00 OUT (0),A 0025 AF XOR A 0026 D3 01 OUT (1),A 0028 3E 03 LD A,3 002A D3 03 OUT (3),A 002C 3E 07 LD A,7 002E D3 02 OUT (2),A 0030 21 5A 01 LD HL,15AH 0033 CD 4B 12 CALL 124BH 0036 3A 3C FB LD A,(0FB3CH) 0039 FE AA CP 0AAH 003B 28 18 JR Z,55H 003D 21 DA 02 LD HL,2DAH 0040 CD 4B 12 CALL 124BH 0043 21 00 80 LD HL,8000H 0046 54 LD D,H 0047 5D LD E,L 0048 13 INC DE 0049 01 FF 7F LD BC,7FFFH 004C 36 00 LD (HL),0 004E ED B0 LDIR 0050 21 3C FB LD HL,0FB3CH END OF DISASSEMBLER RUN. Z>To call the disassembler command only two keys have to be pressed: "M" to select the "M"emory-command-group and "A" to select the dis"A"ssembler. Just in case you are as curious as I am and you like reading other people's code as much as I do, version 0.14a of the monitor is listed in the following (just in case Deft is reading this: There is plenty of room for improvement. :-) There are way too many push/pop instructions etc. But after all it was and still is a pet-project. :-) ): 0000: N8VEM equ 0 ; If set to 1, the monitor is built 0000: ; for the N8VEM, otherwise, the 0000: ; old homebrew Z80 computer is targeted. 0000: FEATURE_BASIC equ 0 ; If set to 0, the BASIC interpreter 0000: ; will not be included. 0000: ;****************************************************************************** 0000: ; 0000: ; Small monitor for the Z80 single board computer consisting of 32 kB ROM 0000: ; ($0000 to $ffff), 32 kB RAM ($8000 to $ffff) and a 16c550 UART. 0000: ; 0000: ; B. Ulmann, 28-SEP-2011, 29-SEP-2011, 01-OCT-2011, 02-OCT-2011, 30-OCT-2011, 0000: ; 01-NOV-2011, 02-NOV-2011, 03-NOV-2011, 06/07/08-JAN-2012 0000: ; I. Kloeckl, 06/07/08-JAN-2012 (FAT implementation for reading files) 0000: ; B. Ulmann, 14-JAN-2011 0000: ; 0000: ; 07-MAR-2012: Fabio Zanicotti spotted an error in the ide_get_id-routine - 0000: ; the value $0a should have been written to ide_lba3 but was 0000: ; instead written into ide_status_cmd (I am quite puzzled why 0000: ; the routine worked nevertheless). This has been corrected. :-) 0000: ; 0000: ; B. Ulmann, 20-MAY-2012 port to N8VEM - no IDE support at the moment 0000: ; 27-MAY-2012 PPIDE support for N8VEM 0000: ; 29-MAY-2012 New system call uart_status introduced (used by the 0000: ; 4th interpreter) 0000: ; 01-JUN-2012 CTRL-Y introduced 0000: ; 03-JUN-2012 F/R command added, UART initialization changed 0000: ; 06-JUN-2012 CAMEL-Forth added 0000: ; 07-JUN-2012 Minor bug fixes (load routine), added rom2ram 0000: ; 16-JUN-2012 stroup added, CAMEL Forth modified to use gets etc. 0000: ; 12-MAY-2013 Added support for the old homebrew Z80 computer. 0000: ; 20-MAY-2013 Added BASIC-subsystem. 0000: ; 05-JUN-2013 Added John Kerr's Z80 disassembler. 0000: ; 28-JUN-2013 Removed BASIC-support for the Z80mini 0000: ; 0000: ; Version 0.14a 0000: ; 0000: ;****************************************************************************** 0000: ; 0000: ; TODO: 0000: ; Read and print IDE error status codes in case of error! 0000: ; 0000: ; Known issues: 0000: ; Memory Dump has a problem when the end address is >= FF00 0000: ; 0000: ;****************************************************************************** 0000: ; 0000: ; RST $00 will enter the monitor (do not care about the return address pushed 0000: ; onto the stack - the stack pointer will be reinitialized during cold as well 0000: ; as during warm starts. 0000: ; 0000: ; Monitor routines will generally called by placing the necessary parameters 0000: ; into some processor registers and then issuing RST $08. More about this later. 0000: ; 0000: ; Whenever a call to the system routine getc is issued, it is tested if the 0000: ; character entered was a CTRL-Y (like in VMS :-) ). If so, a restart of the 0000: ; monitor takes place. Although this interrupt possibility requires a running 0000: ; program to call getc from time to time it is better than nothing since the 0000: ; monitor currently does not take care of interrupts at all. 0000: ; 0000: ; Programs running in memory should not make use of memory above about $FB00 0000: ; to leave some space for the monitor stack. 0000: ; 0000: ; Memory layout is as follows: 0000: ; 0000: ; +-------+ 0000: ; ! $FFFF ! General purpose 512 byte buffer 0000: ; ! --- ! 0000: ; ! $FE00 ! 0000: ; +-------+ 0000: ; ! $DFFF ! FAT control block 0000: ; ! --- ! 0000: ; ! $FDDC ! 0000: ; +-------+ 0000: ; ! $FDDB ! File control block 0000: ; ! --- ! 0000: ; ! $FBBE ! 0000: ; +-------+ 0000: ; ! $FBBD ! 81 byte string buffer 0000: ; ! --- ! 0000: ; ! $FB6D ! 0000: ; +-------+ 0000: ; ! $FB6C ! 12 byte string buffer 0000: ; ! --- ! 0000: ; ! $FB61 ! 0000: ; +-------+ 0000: ; ! $FB60 ! Buffers for various routines 0000: ; ! --- ! 0000: ; ! $FB4D ! 0000: ; +-------+ 0000: ; ! $FB4C ! Scratch area (16 bytes) 0000: ; ! --- ! 0000: ; ! $FB3D ! 0000: ; +-------+ 0000: ; ! $FB3C ! Cold/warm start control (1 byte) 0000: ; +-------+ 0000: ; ! $FB3B ! Stack 0000: ; ! ... ! 0000: ; ! $8000 ! Begin of RAM 0000: ; +-------+ 0000: ; ! $7FFF ! ROM area 0000: ; ! --- ! RST $08 calls a system routine 0000: ; ! $0000 ! RST $00 restarts the monitor 0000: ; +-------+ 0000: ; 0000: ; 0000: LOADABLE equ 0 ; This is necessary for the built-in 0000: ; Forth-interpreter to work in ROM. 0000: monitor_start equ $0000 ; $0000 -> ROM, $8000 -> Test image 0000: #target rom ; ((inserted by zasm)) 0000: #code 0,$10000 ; ((inserted by zasm)) 0000: org monitor_start 0000: ; 0000: ; Definitions used by the monitor and all programs based on this monitor. 0000: ; 0000: ; 01-JUN-2013 B. Ulmann 1st implementation 0000: ; 0000: rom_start equ $0 0000: rom_end equ $7fff 0000: ram_start equ $8000 0000: ram_end equ $ffff 0000: buffer equ ram_end - $1ff ; 512 byte IDE general purpose buffer 0000: last_user_ram equ $f9ff 0000: ; 0000: ; Define the FAT control block memory addresses: 0000: ; 0000: datastart equ buffer - 4 ; Data area start vector 0000: rootstart equ datastart - 4 ; Root directory start vector 0000: fat1start equ rootstart - 4 ; Start vector to first FAT 0000: psiz equ fat1start - 4 ; Size of partition (in sectors) 0000: pstart equ psiz - 4 ; First sector of partition 0000: rootlen equ pstart - 2 ; Maximum number of entries in directory 0000: fatsec equ rootlen - 2 ; FAT size in sectors 0000: ressec equ fatsec - 2 ; Number of reserved sectors 0000: clusiz equ ressec - 1 ; Size of a cluster (in sectors) 0000: fatname equ clusiz - 9 ; Name of the FAT (null terminated) 0000: fatcb equ fatname ; Start of the FATCB 0000: ; 0000: ; Define a file control block (FCB) memory addresses and displacements: 0000: ; 0000: file_buffer equ fatcb - $200 ; 512 byte sector buffer 0000: cluster_sector equ file_buffer - 1 ; Current sector in cluster 0000: current_sector equ cluster_sector - 4 ; Current sector address 0000: current_cluster equ current_sector - 2 ; Current cluster number 0000: file_pointer equ current_cluster - 4 ; Pointer for file position 0000: file_type equ file_pointer - 1 ; 0 -> not found, else OK 0000: first_cluster equ file_type - 2 ; First cluster of file 0000: file_size equ first_cluster - 4 ; Size of file 0000: file_name equ file_size - 12 ; Canonical name of file 0000: fcb equ file_name ; Start of the FCB 0000: ; 0000: fcb_filename equ 0 0000: fcb_file_size equ $c 0000: fcb_first_cluster equ $10 0000: fcb_file_type equ $12 0000: fcb_file_pointer equ $13 0000: fcb_current_cluster equ $17 0000: fcb_current_sector equ $19 0000: fcb_cluster_sector equ $1d 0000: fcb_file_buffer equ $1e 0000: ; 0000: ; We also need some general purpose string buffers: 0000: ; 0000: string_81_bfr equ fcb - 81 0000: string_12_bfr equ string_81_bfr - 12 0000: ; 0000: ; A number of routines need a bit of scratch RAM, too. Since these are 0000: ; sometimes interdependent, each routine gets its own memory cells (only 0000: ; possible since the routines are not recursively called). 0000: ; 0000: load_file_scrat equ string_12_bfr - 2 ; Two bytes for load_file 0000: str2filename_de equ load_file_scrat - 2 ; Two bytes for str2filename 0000: fopen_eob equ str2filename_de - 2 ; Eight bytes for fopen 0000: fopen_rsc equ fopen_eob - 4 0000: fopen_scr equ fopen_rsc - 2 0000: dirlist_scratch equ fopen_scr - 2 ; Eight bytes for fopen 0000: dirlist_eob equ dirlist_scratch - 2 0000: dirlist_rootsec equ dirlist_eob - 4 0000: scratch_area equ dirlist_rootsec - $1 ; Scratch memory (16 byte) 0000: ; 0000: start_type equ scratch_area - $10 ; Distinguish cold/warm start 0000: ; 0000: ; System calls are implemented by rst08 which expects the number of the 0000: ; call to be executed in ix. The numbers of valid calls are defined here: 0000: ; 0000: _cold_start equ $0 0000: _is_hex equ $1 0000: _is_print equ $2 0000: _to_upper equ $3 0000: _crlf equ $4 0000: _getc equ $5 0000: _putc equ $6 0000: _puts equ $7 0000: _strcmp equ $8 0000: _gets equ $9 0000: _fgetc equ $a 0000: _dump_fcb equ $b 0000: _fopen equ $c 0000: _dirlist equ $d 0000: _fatmount equ $e 0000: _fatunmount equ $f 0000: _strchr equ $10 0000: _uart_status equ $11 0000: _getc_nowait equ $12 0000: _print_word equ $13 0000: _print_byte equ $14 0000: _stroup equ $15 0000: _get_word equ $16 0000: ; 0000: ; Some useful ASCII (control) characters: 0000: ; 0000: eos equ $00 ; End of string 0000: cr equ $0d ; Carriage return 0000: lf equ $0a ; Line feed 0000: space equ $20 ; Space 0000: tab equ $09 ; Tabulator 0000: bs equ $08 ; Backspace 0000: bel equ $07 ; Bell 0000: ctrl_y equ 25 ; CTRL-Y character 0000: 0000: #include "mondef.asm" 0000: ; 0000: #if N8VEM = 1 0000: ; 0000: ; 82C55 registers (the 82C55 is used to implement a simple IDE interface): 0000: ; 0000: reg_ppi_base equ $60 ; 82C55 base address 0000: reg_ppi_cntl equ reg_ppi_base + 3 ; 82C55 control register 0000: ; 0000: reg_ppide_lsb equ reg_ppi_base + 0 ; 82C55 port A 0000: reg_ppide_msb equ reg_ppi_base + 1 ; 82C55 port B 0000: reg_ppide_cntl equ reg_ppi_base + 2 ; 82C55 port C 0000: #else 0000: ide_base equ $10 0000: #endif ; N8VEM = 1? 0000: ; 0000: ; 16C550 registers: 0000: ; 0000: #if N8VEM = 1 0000: uart_base equ $68 0000: #else 0000: uart_base equ $0 0000: #endif 0000: ; 0000: uart_register_0 equ uart_base + 0 ; Read/write a character 0000: uart_register_1 equ uart_base + 1 0000: uart_register_2 equ uart_base + 2 0000: uart_register_3 equ uart_base + 3 0000: uart_register_4 equ uart_base + 4 0000: uart_register_5 equ uart_base + 5 ; RX/TX status 0000: uart_register_6 equ uart_base + 6 0000: uart_register_7 equ uart_base + 7 0000: ; 0000: #if N8VEM = 1 0000: ; 0000: ; Memory Page Configuration Latches: 0000: ; 0000: mpcl_ram equ $78 0000: mpcl_rom equ $7c 0000: ; 0000: #endif 0000: ; 0000: ; Main entry point (RST 00H): 0000: ; 0000: F3 rst_00 di ; Disable interrupts 0001: ; 0001: ; The stackpointer will be predecremented by a push instruction. Therefore 0001: ; we set the start of the stack to the end of the reserved memory area in 0001: ; high memory. 0001: ; 0001: 313BFB ld sp, start_type - $1 0004: 1817 jr initialize ; Jump over the RST-area 0006: ; 0006: ; RST-area - here is the main entry point into the monitor. The calling 0006: ; standard looks like this: 0006: ; 0006: ; 1) Set register IX to the number of the system routine to be called. 0006: ; 2) Set the remaining registers according to the routine's documentation. 0006: ; 3) Execute RST $08 to actually call the system routine. 0006: ; 4) Evaluate the values returned in the registers as described by the 0006: ; Routine's documentation. 0006: ; 0006: ; (Currently there are no plans to use more RST entry points, so this routine 0006: ; just runs as long as necessary in memory. If more RSTs will be used, this 0006: ; routine should to be moved to the end of the used ROM area with only a 0006: ; simple jump at the RST $08-location.) 0006: ; 0006: ; This technique of calling system routines can be used as the following 0006: ; example program that just echos characters read from the serial line 0006: ; demonstrates: 0006: ; 0006: ; org $8000 ; Start in lower RAM 0006: ; loop ld ix, 5 ; Prepare call to getc 0006: ; rst 08 ; Execute getc 0006: ; cp 3 ; CTRL-C pressed? 0006: ; jr z, exit ; Yes - exit 0006: ; ld ix, 6 ; Prepare call to putc 0006: ; rst 08 ; Execute putx 0006: ; jr loop ; Process next character 0006: ; exit ld ix, 4 ; Exit - print a CR/LF pair 0006: ; rst 08 ; Call CRLF 0006: ; ld hl, msg ; Pointer to exit message 0006: ; ld ix, 7 ; Prepare calling puts 0006: ; rst 08 ; Call puts 0006: ; rst 00 ; Restart monitor (warm start) 0006: ; msg defb "That's all folks.", $d, $a, 0 0006: ; 0006: ; IMPORTANT: The content of ix is destroyed during the call, so it is NOT 0006: ; possible to perform successive calls to the same system service 0006: ; in a sequence without reloading the ix-register! 0006: ; 0006: ; Currently the following functions are available (a more detailed description 0006: ; can be found in the dispatch table itself - search for the label 0006: ; dispatch_table): 0006: ; 0006: ; $00: cold_start 0006: ; $01: is_hex 0006: ; $02: is_print 0006: ; $03: to_upper 0006: ; $04: crlf 0006: ; $05: getc 0006: ; $06: putc 0006: ; $07: puts 0006: ; $08: strcmp 0006: ; $09: gets 0006: ; $0A: fgetc 0006: ; $0B: dump_fcb 0006: ; $0C: fopen 0006: ; $0D: dirlist 0006: ; $0E: fatmount 0006: ; $0F: fatunmount 0006: ; $10: strchr 0006: ; $11: uart_status 0006: ; $12: getc_nowait 0006: ; $13: print_word 0006: ; $14: print_byte 0006: ; $15: stroup 0006: ; $16: get_word 0006: ; 0006: ; org monitor_start + $08 0006: 00 nop ; Beware: zasm is buggy concerning 0007: 00 nop ; ORG. Therefore we need two nops to 0008: ; get to address $0008. 0008: C5 rst_08 push bc ; Save bc and hl 0009: E5 push hl 000A: DDE5 push ix ; Copy the contents of ix 000C: E1 pop hl ; into hl 000D: 29 add hl, hl ; Double to get displacement in table 000E: 014C1D ld bc, dispatch_table 0011: 09 add hl, bc ; Calculate displacement in table 0012: 4E23462B ld bc, (hl) ; Load bc with the destination address 0016: C5 push bc 0017: DDE1 pop ix ; Load ix with the destination address 0019: E1 pop hl ; Restore hl 001A: C1 pop bc ; and bc 001B: DDE9 jp (ix) ; Jump to the destination routine 001D: ; 001D: ; Initialize UART to 9600,8N1: 001D: ; 001D: 3E80 initialize ld a, $80 001F: D303 out (uart_register_3), a 0021: 3E0C ld a, $c ; 1843200 / (16 * 9600) 0023: D300 out (uart_register_0), a 0025: AF xor a 0026: D301 out (uart_register_1), a 0028: 3E03 ld a, $3 ; 8N1 002A: D303 out (uart_register_3), a 002C: 3E07 ld a, $7 ; FIFO enable, reset RCVR/XMIT FIFO 002E: D302 out (uart_register_2), a 0030: ; 0030: ; Print welcome message: 0030: ; 0030: 215A01 ld hl, hello_msg 0033: CD4B12 call puts 0036: ; 0036: ; If this is a cold start (the location start_type does not contain $aa) 0036: ; all available RAM will be reset to $00 and a message will be printed. 0036: ; 0036: 3A3CFB init_mem ld a, (start_type) 0039: FEAA cp $aa ; Warm start? 003B: 2818 jr z, main_loop ; Yes - enter command loop 003D: 21DA02 ld hl, cold_start_msg 0040: CD4B12 call puts ; Print cold start message 0043: 210080 ld hl, ram_start ; Start of block to be filled with $00 0046: 545D ld de, hl ; End address of block 0048: 13 inc de ; plus 1 (for ldir) 0049: 01FF7F ld bc, ram_end - ram_start 004C: 3600 ld (hl), $00 ; Load first memory location 004E: EDB0 ldir ; And copy this value down 0050: 213CFB ld hl, start_type 0053: 36AA ld (hl), $aa ; Cold start done, remember this 0055: ; 0055: ; Read characters from the serial line and interpret them: 0055: ; 0055: 216202 main_loop ld hl, monitor_prompt 0058: CD4B12 call puts 005B: ; 005B: ; The monitor is rather simple: All commands consist of one or two letters. 005B: ; The first character selects a command group, the second the desired command 005B: ; out of that group. When a command is recognized, it will be spelled out 005B: ; automatically and the user will be prompted for arguments if applicable. 005B: ; 005B: CDF902 call monitor_key ; Read a key 005E: ; Which group did we get? 005E: FE43 cp 'C' ; Control group? 0060: 2022 jr nz, disk_group ; No - test next group 0062: 216802 ld hl, cg_msg ; Print group prompt 0065: CD4B12 call puts 0068: CDF902 call monitor_key ; Get command key 006B: FE43 cp 'C' ; Cold start? 006D: CA1C14 jp z, cold_start 0070: FE57 cp 'W' ; Warm start? 0072: CA2114 jp z, warm_start 0075: FE53 cp 'S' ; Start? 0077: CAC610 jp z, start 007A: FE49 cp 'I' ; Info? 007C: CC8B0D call z, info 007F: 28D4 jr z, main_loop 0081: C34E01 jp cmd_error ; Unknown control-group-command 0084: FE44 disk_group cp 'D' ; Disk group? 0086: 2028 jr nz, file_group ; No - file group? 0088: 217102 ld hl, dg_msg ; Print group prompt 008B: CD4B12 call puts 008E: CDF902 call monitor_key ; Get command 0091: FE49 cp 'I' ; Info? 0093: CC1B08 call z, disk_info 0096: 28BD jr z, main_loop 0098: FE4D cp 'M' ; Mount? 009A: CC7C0F call z, mount 009D: 28B6 jr z, main_loop 009F: FE54 cp 'T' ; Read from disk? 00A1: CC4708 call z, disk_transfer 00A4: 28AF jr z, main_loop 00A6: FE55 cp 'U' ; Unmount? 00A8: CCE010 call z, unmount 00AB: 28A8 jr z, main_loop 00AD: C34E01 jp cmd_error ; Unknown disk-group-command 00B0: FE46 file_group cp 'F' ; File group? 00B2: 2028 jr nz, help_group ; No - help group? 00B4: 217702 ld hl, fg_msg ; Print group prompt 00B7: CD4B12 call puts 00BA: CDF902 call monitor_key ; Get command 00BD: FE43 cp 'C' ; Cat? 00BF: CC0A03 call z, cat_file 00C2: 2891 jr z, main_loop 00C4: FE44 cp 'D' ; Directory? 00C6: CC4903 call z, directory 00C9: 288A jr z, main_loop 00CB: FE4C cp 'L' ; Load? 00CD: CCDB0E call z, load_file 00D0: 2883 jr z, main_loop 00D2: FE52 cp 'R' ; Run an executable? 00D4: CC6A0E call z, load_and_run 00D7: CA5500 jp z, main_loop 00DA: 1872 jr cmd_error ; Unknown file-group-command 00DC: FE48 help_group cp 'H' ; Help? (No further level expected.) 00DE: CCC40A call z, help ; Yes :-) 00E1: CA5500 jp z, main_loop 00E4: FE4D memory_group cp 'M' ; Memory group? 00E6: C23401 jp nz, subsys_group; No - subsystem group? 00E9: 217D02 ld hl, mg_msg ; Print group prompt 00EC: CD4B12 call puts 00EF: CDF902 call monitor_key ; Get command key 00F2: FE41 cp 'A' ; Disassemble? 00F4: CC6103 call z, disassemble 00F7: CA5500 jp z, main_loop 00FA: FE44 cp 'D' ; Dump? 00FC: CC5409 call z, dump 00FF: CA5500 jp z, main_loop 0102: FE45 cp 'E' ; Examine? 0104: CCCA09 call z, examine 0107: CA5500 jp z, main_loop 010A: FE46 cp 'F' ; Fill? 010C: CC1D0A call z, fill 010F: CA5500 jp z, main_loop 0112: FE49 cp 'I' ; INTEL-Hex load? 0114: CCD50C call z, ih_load 0117: CA5500 jp z, main_loop 011A: FE4C cp 'L' ; Load? 011C: CCA10D call z, load 011F: CA5500 jp z, main_loop 0122: FE4D cp 'M' ; Move? 0124: CC920F call z, move 0127: CA5500 jp z, main_loop 012A: FE52 cp 'R' ; Register dump? 012C: CC0A10 call z, rdump 012F: CA5500 jp z, main_loop 0132: #if N8VEM = 1 0132: cp 'S' ; Switch ROM to RAM? 0132: call z, rom2ram ; This routine won't return 0132: jp z, main_loop 0132: #endif 0132: 181A jr cmd_error ; Unknown memory-group-command 0134: FE53 subsys_group cp 'S' ; Subsystem group? 0136: C24901 jp nz, group_error ; No - print an error message 0139: 218502 ld hl, sg_msg ; Print group prompt 013C: CD4B12 call puts 013F: CDF902 call monitor_key ; Get command key 0142: FE46 cp 'F' ; Forth? 0144: CA7A1D jp z, forth_subsystem 0147: #if FEATURE_BASIC = 1 0147: cp 'B' ; BASIC? 0147: jp z, basic_subsystem 0147: #endif 0147: 1805 jr cmd_error ; Unknown subsytem-group-command 0149: 21B602 group_error ld hl, group_err_msg 014C: 1803 jr print_error 014E: 219002 cmd_error ld hl, command_err_msg 0151: CD4012 print_error call putc ; Echo the illegal character 0154: CD4B12 call puts ; and print the error message 0157: C35500 jp main_loop 015A: ; 015A: ; Some constants for the monitor: 015A: ; 015A: 0D0A0D0A 015E: 53696D70 0162: 6C65205A 0166: 38302D6D 016A: 6F6E6974 016E: 6F72202D 0172: 20562030 0176: 2E313461 017A: 20 hello_msg defb cr, lf, cr, lf, "Simple Z80-monitor - V 0.14a " 017B: 28422E20 017F: 556C6D61 0183: 6E6E2C20 0187: 53657074 018B: 656D6265 018F: 72203230 0193: 3131202D 0197: 204A756E 019B: 65203230 019F: 3133290D 01A3: 0A defb "(B. Ulmann, September 2011 - June 2013)", cr, lf 01A4: 20202054 01A8: 68697320 01AC: 6D6F6E69 01B0: 746F7220 01B4: 636F6E74 01B8: 61696E73 01BC: 20427261 01C0: 6420526F 01C4: 64726967 01C8: 75657A27 01CC: 20 defb " This monitor contains Brad Rodriguez' " 01CD: 43414D45 01D1: 4C20466F 01D5: 7274682C 01D9: 200D0A defb "CAMEL Forth, ", cr, lf 01DC: 20202020 ... 01F5: 4A6F686E 01F9: 204B6572 01FD: 72277320 0201: 5A383020 defb " John Kerr's Z80 " 0205: 64697361 0209: 7373656D 020D: 626C6572 defb "disassembler" 0211: #if FEATURE_BASIC = 1 0211: defb ", and", cr, lf 0211: defb " BASIC 4.7 (C) Microsoft" 0211: #endif 0211: 0D0A defb cr, lf 0213: 2D2D2D2D ... 023B: defb "----------------------------------------" 023B: 2D2D2D2D ... 0262: defb "---------------------------------------" 0262: 0D0A5A3E 0266: 2000 monitor_prompt defb cr, lf, "Z> ", eos 0268: 434F4E54 026C: 524F4C2F 0270: 00 cg_msg defb "CONTROL/", eos 0271: 4449534B 0275: 2F00 dg_msg defb "DISK/", eos 0277: 46494C45 027B: 2F00 fg_msg defb "FILE/", eos 027D: 4D454D4F 0281: 52592F00 mg_msg defb "MEMORY/", eos 0285: 53554253 0289: 59535445 028D: 4D2F00 sg_msg defb "SUBSYSTEM/", eos 0290: 3A205379 0294: 6E746178 0298: 20657272 029C: 6F72202D 02A0: 20636F6D 02A4: 6D616E64 02A8: 206E6F74 02AC: 20666F75 02B0: 6E64210D 02B4: 0A00 command_err_msg defb ": Syntax error - command not found!", cr, lf, eos 02B6: 3A205379 02BA: 6E746178 02BE: 20657272 02C2: 6F72202D 02C6: 2067726F 02CA: 7570206E 02CE: 6F742066 02D2: 6F756E64 02D6: 210D0A00 group_err_msg defb ": Syntax error - group not found!", cr, lf, eos 02DA: 436F6C64 02DE: 20737461 02E2: 72742C20 02E6: 636C6561 02EA: 72696E67 02EE: 206D656D 02F2: 6F72792E 02F6: 0D0A00 cold_start_msg defb "Cold start, clearing memory.", cr, lf, eos 02F9: ; 02F9: ; Read a key for command group and command (this routine differs slightly 02F9: ; from getc as it can also return to the monitor prompt with a dirty trick 02F9: ; readjusting the stack): 02F9: ; 02F9: CD5911 monitor_key call getc 02FC: FE0A cp lf ; Ignore LF 02FE: 28F9 jr z, monitor_key ; Just get the next character 0300: CD4311 call to_upper 0303: FE0D cp cr ; A CR will return to the prompt 0305: C0 ret nz ; No - just return 0306: 33 inc sp ; Correct SP to and avoid ret! 0307: C35500 jp main_loop 030A: ; 030A: ;****************************************************************************** 030A: ;*** 030A: ;*** The following routines are used in the interactive part of the monitor 030A: ;*** 030A: ;****************************************************************************** 030A: ; 030A: ; Print a file's contents to STDOUT: 030A: ; 030A: C5 cat_file push bc 030B: D5 push de 030C: E5 push hl 030D: FDE5 push iy 030F: 213A03 ld hl, cat_file_prompt 0312: CD4B12 call puts 0315: 216DFB ld hl, string_81_bfr 0318: 0651 ld b, 81 031A: CDBA11 call gets ; Read the filename into buffer 031D: CD5B12 call stroup ; Convert to upper case 0320: FD21BEFB ld iy, fcb ; Prepare fopen (only one FCB currently) 0324: 1161FB ld de, string_12_bfr 0327: CD1C17 call fopen 032A: CD6114 cat_file_loop call fgetc ; Get a single character 032D: 3805 jr c, cat_file_exit 032F: CD4012 call putc ; Print character if not EOF 0332: 18F6 jr cat_file_loop ; Next character 0334: FDE1 cat_file_exit pop iy 0336: E1 pop hl 0337: D1 pop de 0338: C1 pop bc 0339: C9 ret 033A: 4341543A 033E: 2046494C 0342: 454E414D 0346: 453D00 cat_file_prompt defb "CAT: FILENAME=", eos 0349: ; 0349: ; directory - a simple wrapper for dirlist (necessary for printing the command 0349: ; name) 0349: ; 0349: E5 directory push hl 034A: 215503 ld hl, directory_msg 034D: CD4B12 call puts 0350: CD6F18 call dirlist 0353: E1 pop hl 0354: C9 ret 0355: 44495245 0359: 43544F52 035D: 590D0A00 directory_msg defb "DIRECTORY", cr, lf, eos 0361: ; 0361: ; Disassemble a memory area 0361: ; 0361: F5 disassemble push af 0362: C5 push bc 0363: D5 push de 0364: E5 push hl 0365: DDE5 push ix 0367: 219D03 ld hl, dismsg1 ; Prompt for the start address 036A: CD4B12 call puts 036D: CDAF11 call get_word ; Read user input 0370: E5 push hl ; Save start address for later 0371: 21B103 ld hl, dismsg2 ; Prompt for end address 0374: CD4B12 call puts 0377: CDAF11 call get_word 037A: CD4C11 call crlf 037D: CD4C11 call crlf 0380: D1 pop de ; Start address -> de 0381: E5 push hl ; Push end address 0382: CDD403 disloop call disz80 ; Disassemble one instruction 0385: CD4C11 call crlf 0388: E1 pop hl 0389: E5 push hl 038A: A7 and a ; Clear carry, just in case 038B: ED52 sbc hl, de ; End address reached? 038D: 30F3 jr nc, disloop ; No, continue disassembling 038F: 21B703 ld hl, dismsg3 ; Yes, print end message 0392: CD4B12 call puts 0395: E1 pop hl ; Remove the end address copy 0396: DDE1 pop ix 0398: E1 pop hl 0399: D1 pop de 039A: C1 pop bc 039B: F1 pop af 039C: C9 ret 039D: 44495341 03A1: 5353454D 03A5: 424C453A 03A9: 20535441 03AD: 52543D00 dismsg1 defb "DISASSEMBLE: START=", eos 03B1: 20454E44 03B5: 3D00 dismsg2 defb " END=", eos 03B7: 0D0A454E 03BB: 44204F46 03BF: 20444953 03C3: 41535345 03C7: 4D424C45 03CB: 52205255 03CF: 4E2E0D0A 03D3: 00 dismsg3 defb cr, lf, "END OF DISASSEMBLER RUN.", cr, lf, eos ;... ...John Kerr's Disassembler resides here... ... 081A: #include "../disassembler/dis_z80.asm" 081B: ; 081B: ; 081B: ; Get and print disk info: 081B: ; 081B: F5 disk_info push af 081C: E5 push hl 081D: 213F08 ld hl, disk_info_msg 0820: CD4B12 call puts 0823: CDBC12 call ide_get_id ; Read the disk info into the IDE buffer 0826: 2113FE ld hl, buffer + $13 0829: 3609 ld (hl), tab 082B: CD4B12 call puts ; Print vendor information 082E: CD4C11 call crlf 0831: 212DFE ld hl, buffer + $2d 0834: 3609 ld (hl), tab 0836: CD4B12 call puts 0839: CD4C11 call crlf 083C: E1 pop hl 083D: F1 pop af 083E: C9 ret 083F: 494E464F 0843: 3A0D0A00 disk_info_msg defb "INFO:", cr, lf, eos 0847: ; 0847: ; Read data from disk to memory 0847: ; 0847: F5 disk_transfer push af 0848: C5 push bc 0849: D5 push de 084A: E5 push hl 084B: DDE5 push ix 084D: 21C608 ld hl, disk_trx_msg_0 0850: CD4B12 call puts ; Print Read/Write prompt 0853: CD5911 disk_trx_rwlp call getc 0856: CD4311 call to_upper 0859: FE52 cp 'R' ; Read? 085B: 2009 jr nz, disk_trx_nr ; No 085D: DD215613 ld ix, ide_rs ; Yes, we will call ide_rs later 0861: 21D008 ld hl, disk_trx_msg_1r 0864: 180B jr disk_trx_main ; Prompt the user for parameters 0866: FE57 disk_trx_nr cp 'W' ; Write? 0868: 20E9 jr nz, disk_trx_rwlp 086A: DD21C413 ld ix, ide_ws ; Yes, we will call ide_ws later 086E: 21EA08 ld hl, disk_trx_msg_1w 0871: CD4B12 disk_trx_main call puts ; Print start address prompt 0874: CDAF11 call get_word ; Get memory start address 0877: E5 push hl 0878: 210509 ld hl, disk_trx_msg_2 087B: CD4B12 call puts ; Prompt for number of blocks 087E: CD8A11 call get_byte ; There are only 128 block of memory! 0881: FE00 cp 0 ; Did the user ask for 00 blocks? 0883: 2008 jr nz, disk_trx_1 ; No, continue prompting 0885: 213209 ld hl, disk_trx_msg_4 0888: CD4B12 call puts 088B: 1830 jr disk_trx_exit 088D: 212309 disk_trx_1 ld hl, disk_trx_msg_3 0890: CD4B12 call puts ; Prompt for disk start sector 0893: CDAF11 call get_word ; This is a four byte address! 0896: 444D ld bc, hl 0898: CDAF11 call get_word 089B: 545D ld de, hl 089D: E1 pop hl ; Restore memory start address 089E: ; Register contents: 089E: ; A: Number of blocks 089E: ; BC: LBA3/2 089E: ; DE: LBA1/0 089E: ; HL: Memory start address 089E: F5 disk_trx_loop push af ; Save number of sectors 089F: CDC408 call disk_trampoline ; Read/write one sector (F is changed!) 08A2: E5 push hl ; Save memory address 08A3: C5 push bc ; Save LBA3/2 08A4: 626B ld hl, de ; Increment DE (LBA1/0) 08A6: 010100 ld bc, $0001 ; by one and 08A9: 09 add hl, bc ; generate a carry if necessary 08AA: 545D ld de, hl ; Save new LBA1/0 08AC: E1 pop hl ; Restore LBA3/2 into HL (!) 08AD: 3001 jr nc, disk_trx_skip 08AF: 09 add hl, bc ; Increment BC if there was a carry 08B0: 444D disk_trx_skip ld bc, hl ; Write new LBA3/2 into BC 08B2: E1 pop hl ; Restore memory address 08B3: C5 push bc ; Save LBA3/2 08B4: 010002 ld bc, $200 ; 512 byte per block 08B7: 09 add hl, bc ; Set pointer to next memory block 08B8: C1 pop bc ; Restore LBA3/2 08B9: F1 pop af 08BA: 3D dec a ; One block already done 08BB: 20E1 jr nz, disk_trx_loop 08BD: DDE1 disk_trx_exit pop ix 08BF: E1 pop hl 08C0: D1 pop de 08C1: C1 pop bc 08C2: F1 pop af 08C3: C9 ret 08C4: DDE9 disk_trampoline jp (ix) 08C6: 5452414E 08CA: 53464552 08CE: 2F00 disk_trx_msg_0 defb "TRANSFER/", eos 08D0: 52454144 08D4: 3A200D0A 08D8: 20202020 08DC: 4D454D4F 08E0: 52592053 08E4: 54415254 08E8: 3D00 disk_trx_msg_1r defb "READ: ", cr, lf, " MEMORY START=", eos 08EA: 57524954 08EE: 453A200D 08F2: 0A202020 08F6: 204D454D 08FA: 4F525920 08FE: 53544152 0902: 543D00 disk_trx_msg_1w defb "WRITE: ", cr, lf, " MEMORY START=", eos 0905: 204E554D 0909: 42455220 090D: 4F462042 0911: 4C4F434B 0915: 53202835 0919: 31322042 091D: 59544529 0921: 3D00 disk_trx_msg_2 defb " NUMBER OF BLOCKS (512 BYTE)=", eos 0923: 20535441 0927: 52542053 092B: 4543544F 092F: 523D00 disk_trx_msg_3 defb " START SECTOR=", eos 0932: 204E6F74 0936: 68696E67 093A: 20746F20 093E: 646F2066 0942: 6F72207A 0946: 65726F20 094A: 626C6F63 094E: 6B732E0D 0952: 0A00 disk_trx_msg_4 defb " Nothing to do for zero blocks.", cr, lf, eos 0954: ; 0954: ; Dump a memory area 0954: ; 0954: F5 dump push af 0955: C5 push bc 0956: D5 push de 0957: E5 push hl 0958: 21B409 ld hl, dump_msg_1 095B: CD4B12 call puts ; Print prompt 095E: CDAF11 call get_word ; Read start address 0961: E5 push hl ; Save start address 0962: 21C109 ld hl, dump_msg_2 ; Prompt for end address 0965: CD4B12 call puts 0968: CDAF11 call get_word ; Get end address 096B: CD4C11 call crlf 096E: 23 inc hl ; Increment stop address for comparison 096F: 545D ld de, hl ; DE now contains the stop address 0971: E1 pop hl ; HL is the start address again 0972: ; This loop will dump 16 memory locations at once - even 0972: ; if this turns out to be more than requested. 0972: 0610 dump_line ld b, $10 ; This loop will process 16 bytes 0974: E5 push hl ; Save HL again 0975: CD3312 call print_word ; Print address 0978: 21C709 ld hl, dump_msg_3 ; and a colon 097B: CD4B12 call puts 097E: E1 pop hl ; Restore address 097F: E5 push hl ; We will need HL for the ASCII dump 0980: 7E dump_loop ld a, (hl) ; Get the memory content 0981: CD1212 call print_byte ; and print it 0984: 3E20 ld a, ' ' ; Print a space 0986: CD4012 call putc 0989: 23 inc hl ; Increment address counter 098A: 10F4 djnz dump_loop ; Continue with this line 098C: ; This loop will dump the very same 16 memory locations - but 098C: ; this time printable ASCII characters will be written. 098C: 0610 ld b, $10 ; 16 characters at a time 098E: 3E20 ld a, ' ' ; We need some spaces 0990: CD4012 call putc ; to print 0993: CD4012 call putc 0996: E1 pop hl ; Restore the start address 0997: 7E dump_ascii_loop ld a, (hl) ; Get byte 0998: CD0A11 call is_print ; Is it printable? 099B: 3802 jr c, dump_al_1 ; Yes 099D: 3E2E ld a, '.' ; No - print a dot 099F: CD4012 dump_al_1 call putc ; Print the character 09A2: 23 inc hl ; Increment address to read from 09A3: 10F2 djnz dump_ascii_loop 09A5: ; Now we are finished with printing one line of dump output. 09A5: CD4C11 call crlf ; CR/LF for next line on terminal 09A8: E5 push hl ; Save the current address for later 09A9: A7 and a ; Clear carry 09AA: ED52 sbc hl, de ; Have we reached the last address? 09AC: E1 pop hl ; restore the address 09AD: 38C3 jr c, dump_line ; Dump next line of 16 bytes 09AF: E1 pop hl 09B0: D1 pop de 09B1: C1 pop bc 09B2: F1 pop af 09B3: C9 ret 09B4: 44554D50 09B8: 3A205354 09BC: 4152543D 09C0: 00 dump_msg_1 defb "DUMP: START=", eos 09C1: 20454E44 09C5: 3D00 dump_msg_2 defb " END=", eos 09C7: 3A2000 dump_msg_3 defb ": ", eos 09CA: ; 09CA: ; Examine a memory location: 09CA: ; 09CA: F5 examine push af 09CB: E5 push hl 09CC: 21F809 ld hl, examine_msg_1 09CF: CD4B12 call puts 09D2: CDAF11 call get_word ; Wait for a four-nibble address 09D5: E5 push hl ; Save address for later 09D6: 21160A ld hl, examine_msg_2 09D9: CD4B12 call puts 09DC: E1 examine_loop pop hl ; Restore address 09DD: 7E ld a, (hl) ; Get content of address 09DE: 23 inc hl ; Prepare for next examination 09DF: E5 push hl ; Save hl again for later use 09E0: CD1212 call print_byte ; Print the byte 09E3: CD5911 call getc ; Get a character 09E6: FE20 cp ' ' ; A blank? 09E8: 2007 jr nz, examine_exit; No - exit 09EA: 3E20 ld a, ' ' ; Print a blank character 09EC: CD4012 call putc 09EF: 18EB jr examine_loop 09F1: E1 examine_exit pop hl ; Get rid of save hl value 09F2: CD4C11 call crlf ; Print CR/LF 09F5: E1 pop hl 09F6: F1 pop af 09F7: C9 ret 09F8: 4558414D 09FC: 494E4520 0A00: 28747970 0A04: 65202720 0A08: 272F5245 0A0C: 54293A20 0A10: 41444452 0A14: 3D00 examine_msg_1 defb "EXAMINE (type ' '/RET): ADDR=", eos 0A16: 20444154 0A1A: 413D00 examine_msg_2 defb " DATA=", eos 0A1D: ; 0A1D: ; Fill a block of memory with a single byte - the user is prompted for the 0A1D: ; start address, the length of the block and the fill value. 0A1D: ; 0A1D: F5 fill push af ; We will need nearly all registers 0A1E: C5 push bc 0A1F: D5 push de 0A20: E5 push hl 0A21: 21770A ld hl, fill_msg_1 ; Prompt for start address 0A24: CD4B12 call puts 0A27: CDAF11 call get_word ; Get the start address 0A2A: E5 push hl ; Store the start address 0A2B: A7 and a ; Clear carry 0A2C: 010080 ld bc, ram_start 0A2F: ED42 sbc hl, bc ; Is the address in the RAM area? 0A31: 3009 jr nc, fill_get_length 0A33: 21950A ld hl, fill_msg_4 ; No! 0A36: CD4B12 call puts ; Print error message 0A39: E1 pop hl ; Clean up the stack 0A3A: 1836 jr fill_exit ; Leave routine 0A3C: 21840A fill_get_length ld hl, fill_msg_2 ; Prompt for length information 0A3F: CD4B12 call puts 0A42: CDAF11 call get_word ; Get the length of the block 0A45: ; Now make sure that start + length is still in RAM: 0A45: 444D ld bc, hl ; BC contains the length 0A47: E1 pop hl ; HL now contains the start address 0A48: E5 push hl ; Save the start address again 0A49: C5 push bc ; Save the length 0A4A: 09 add hl, bc ; Start + length 0A4B: A7 and a ; Clear carry 0A4C: 010080 ld bc, ram_start 0A4F: ED42 sbc hl, bc ; Compare with ram_start 0A51: 300A jr nc, fill_get_value 0A53: 21A90A ld hl, fill_msg_5 ; Print error message 0A56: CD4B12 call puts 0A59: C1 pop bc ; Clean up the stack 0A5A: E1 pop hl 0A5B: 1815 jr fill_exit ; Leave the routine 0A5D: 218D0A fill_get_value ld hl, fill_msg_3 ; Prompt for fill value 0A60: CD4B12 call puts 0A63: CD8A11 call get_byte ; Get the fill value 0A66: C1 pop bc ; Get the length from the stack 0A67: E1 pop hl ; Get the start address again 0A68: 545D ld de, hl ; DE = HL + 1 0A6A: 13 inc de 0A6B: 0B dec bc 0A6C: ; HL = start address 0A6C: ; DE = destination address = HL + 1 0A6C: ; Please note that this is necessary - LDIR does not 0A6C: ; work with DE == HL. :-) 0A6C: ; A = fill value 0A6C: 77 ld (hl), a ; Store A into first memory location 0A6D: EDB0 ldir ; Fill the memory 0A6F: CD4C11 call crlf 0A72: E1 fill_exit pop hl ; Restore the register contents 0A73: D1 pop de 0A74: C1 pop bc 0A75: F1 pop af 0A76: C9 ret 0A77: 46494C4C 0A7B: 3A205354 0A7F: 4152543D 0A83: 00 fill_msg_1 defb "FILL: START=", eos 0A84: 204C454E 0A88: 4754483D 0A8C: 00 fill_msg_2 defb " LENGTH=", eos 0A8D: 2056414C 0A91: 55453D00 fill_msg_3 defb " VALUE=", eos 0A95: 20496C6C 0A99: 6567616C 0A9D: 20616464 0AA1: 72657373 0AA5: 210D0A00 fill_msg_4 defb " Illegal address!", cr, lf, eos 0AA9: 20426C6F 0AAD: 636B2065 0AB1: 78636565 0AB5: 64732052 0AB9: 414D2061 0ABD: 72656121 0AC1: 0D0A00 fill_msg_5 defb " Block exceeds RAM area!", cr, lf, eos 0AC4: ; 0AC4: ; Help 0AC4: ; 0AC4: E5 help push hl 0AC5: 21CD0A ld hl, help_msg 0AC8: CD4B12 call puts 0ACB: E1 pop hl 0ACC: C9 ret 0ACD: 48454C50 0AD1: 3A204B6E 0AD5: 6F776E20 0AD9: 636F6D6D 0ADD: 616E6420 0AE1: 67726F75 0AE5: 70732061 0AE9: 6E642063 0AED: 6F6D6D61 0AF1: 6E64733A 0AF5: 0D0A help_msg defb "HELP: Known command groups and commands:", cr, lf 0AF7: 0D0A defb cr, lf 0AF9: 20202020 ... 0B02: 43286F6E 0B06: 74726F6C 0B0A: 2067726F 0B0E: 7570293A 0B12: 0D0A defb " C(ontrol group):", cr, lf 0B14: 20202020 ... 0B21: 43286F6C 0B25: 64207374 0B29: 61727429 0B2D: 2C204928 0B31: 6E666F29 0B35: 2C205328 0B39: 74617274 0B3D: 292C20 defb " C(old start), I(nfo), S(tart), " 0B40: 57286172 0B44: 6D207374 0B48: 61727429 0B4C: 0D0A defb "W(arm start)", cr, lf 0B4E: 20202020 ... 0B57: 44286973 0B5B: 6B206772 0B5F: 6F757029 0B63: 3A0D0A defb " D(isk group):", cr, lf 0B66: 20202020 ... 0B73: 49286E66 0B77: 6F292C20 0B7B: 4D286F75 0B7F: 6E74292C 0B83: 20542872 0B87: 616E7366 0B8B: 6572292C defb " I(nfo), M(ount), T(ransfer)," 0B8F: 2055286E 0B93: 6D6F756E 0B97: 74290D0A defb " U(nmount)", cr, lf 0B9B: 20202020 ... 0BA8: 52286561 0BAC: 64292C20 0BB0: 57287269 0BB4: 7465290D 0BB8: 0A defb " R(ead), W(rite)", cr, lf 0BB9: 20202020 ... 0BC2: 4628696C 0BC6: 65206772 0BCA: 6F757029 0BCE: 3A0D0A defb " F(ile group):", cr, lf 0BD1: 20202020 ... 0BDE: 43286174 0BE2: 292C2044 0BE6: 28697265 0BEA: 63746F72 0BEE: 79292C20 0BF2: 4C286F61 0BF6: 64292C20 0BFA: 5228756E 0BFE: 290D0A defb " C(at), D(irectory), L(oad), R(un)", cr, lf 0C01: 20202020 ... 0C0A: 4828656C 0C0E: 70290D0A defb " H(elp)", cr, lf 0C12: 20202020 ... 0C1B: 4D28656D 0C1F: 6F727920 0C23: 67726F75 0C27: 70293A0D 0C2B: 0A defb " M(emory group):", cr, lf 0C2C: 20202020 ... 0C39: 28646973 0C3D: 29412873 0C41: 73656D62 0C45: 6C65292C 0C49: 20442875 0C4D: 6D70292C 0C51: 20452878 0C55: 616D696E 0C59: 65292C20 defb " (dis)A(ssemble), D(ump), E(xamine), " 0C5D: 4628696C 0C61: 6C292C20 0C65: 49286E74 0C69: 656C2048 0C6D: 6578204C 0C71: 6F616429 0C75: 2C200D0A defb "F(ill), I(ntel Hex Load), ", cr, lf 0C79: 20202020 ... 0C86: 4C286F61 0C8A: 64292C20 0C8E: 52286567 0C92: 69737465 0C96: 72206475 0C9A: 6D70292C 0C9E: 20 defb " L(oad), R(egister dump), " 0C9F: #if N8VEM = 1 0C9F: defb " S(witch ROM to RAM)", cr, lf 0C9F: #else 0C9F: 0D0A defb cr, lf 0CA1: #endif 0CA1: 20202020 ... 0CAA: 53287562 0CAE: 73797374 0CB2: 656D2067 0CB6: 726F7570 0CBA: 293A0D0A defb " S(ubsystem group):", cr, lf 0CBE: 20202020 ... 0CCB: 46286F72 0CCF: 746829 defb " F(orth)" 0CD2: #if FEATURE_BASIC = 1 0CD2: defb ", B(ASIC)" 0CD2: #endif 0CD2: 0D0A00 defb cr, lf, eos 0CD5: ; 0CD5: ; Load an INTEL-Hex file (a ROM image) into memory. This routine has been 0CD5: ; more or less stolen from a boot program written by Andrew Lynch and adapted 0CD5: ; to this simple Z80 based machine. 0CD5: ; 0CD5: ; The INTEL-Hex format looks a bit awkward - a single line contains these 0CD5: ; parts: 0CD5: ; ':', Record length (2 hex characters), load address field (4 hex characters), 0CD5: ; record type field (2 characters), data field (2 * n hex characters), 0CD5: ; checksum field. Valid record types are 0 (data) and 1 (end of file). 0CD5: ; 0CD5: ; Please note that this routine will not echo what it read from stdin but 0CD5: ; what it "understood". :-) 0CD5: ; 0CD5: F5 ih_load push af 0CD6: D5 push de 0CD7: E5 push hl 0CD8: 21580D ld hl, ih_load_msg_1 0CDB: CD4B12 call puts 0CDE: CD5911 ih_load_loop call getc ; Get a single character 0CE1: FE0D cp cr ; Don't care about CR 0CE3: 28F9 jr z, ih_load_loop 0CE5: FE0A cp lf ; ...or LF 0CE7: 28F5 jr z, ih_load_loop 0CE9: FE20 cp space ; ...or a space 0CEB: 28F1 jr z, ih_load_loop 0CED: CD4311 call to_upper ; Convert to upper case 0CF0: CD4012 call putc ; Echo character 0CF3: FE3A cp ':' ; Is it a colon? 0CF5: 204E jr nz, ih_load_error 0CF7: CD8A11 call get_byte ; Get record length into A 0CFA: 57 ld d, a ; Length is now in D 0CFB: 1E00 ld e, $0 ; Clear checksum 0CFD: CD520D call ih_load_chk ; Compute checksum 0D00: CDAF11 call get_word ; Get load address into HL 0D03: 7C ld a, h ; Update checksum by this address 0D04: CD520D call ih_load_chk 0D07: 7D ld a, l 0D08: CD520D call ih_load_chk 0D0B: CD8A11 call get_byte ; Get the record type 0D0E: CD520D call ih_load_chk ; Update checksum 0D11: FE01 cp $1 ; Have we reached the EOF marker? 0D13: 2012 jr nz, ih_load_data; No - get some data 0D15: CD8A11 call get_byte ; Yes - EOF, read checksum data 0D18: CD520D call ih_load_chk ; Update our own checksum 0D1B: 7B ld a, e 0D1C: A7 and a ; Is our checksum zero (as expected)? 0D1D: 282C jr z, ih_load_exit ; Yes - exit this routine 0D1F: 217A0D ih_load_chk_err ld hl, ih_load_msg_3 0D22: CD4B12 call puts ; No - print an error message 0D25: 1824 jr ih_load_exit ; and exit 0D27: 7A ih_load_data ld a, d ; Record length is now in A 0D28: A7 and a ; Did we process all bytes? 0D29: 280B jr z, ih_load_eol ; Yes - process end of line 0D2B: CD8A11 call get_byte ; Read two hex digits into A 0D2E: CD520D call ih_load_chk ; Update checksum 0D31: 77 ld (hl), a ; Store byte into memory 0D32: 23 inc hl ; Increment pointer 0D33: 15 dec d ; Decrement remaining record length 0D34: 18F1 jr ih_load_data ; Get next byte 0D36: CD8A11 ih_load_eol call get_byte ; Read the last byte in the line 0D39: CD520D call ih_load_chk ; Update checksum 0D3C: 7B ld a, e 0D3D: A7 and a ; Is the checksum zero (as expected)? 0D3E: 20DF jr nz, ih_load_chk_err 0D40: CD4C11 call crlf 0D43: 1899 jr ih_load_loop ; Yes - read next line 0D45: 216B0D ih_load_error ld hl, ih_load_msg_2 0D48: CD4B12 call puts ; Print error message 0D4B: CD4C11 ih_load_exit call crlf 0D4E: E1 pop hl ; Restore registers 0D4F: D1 pop de 0D50: F1 pop af 0D51: C9 ret 0D52: ; 0D52: 4F ih_load_chk ld c, a ; All in all compute E = E - A 0D53: 7B ld a, e 0D54: 91 sub c 0D55: 5F ld e, a 0D56: 79 ld a, c 0D57: C9 ret 0D58: 494E5445 0D5C: 4C204845 0D60: 58204C4F 0D64: 41443A20 0D68: 0D0A00 ih_load_msg_1 defb "INTEL HEX LOAD: ", cr, lf, eos 0D6B: 2053796E 0D6F: 74617820 0D73: 6572726F 0D77: 722100 ih_load_msg_2 defb " Syntax error!", eos 0D7A: 20436865 0D7E: 636B7375 0D82: 6D206572 0D86: 726F7221 0D8A: 00 ih_load_msg_3 defb " Checksum error!", eos 0D8B: ; 0D8B: ; Print version information etc. 0D8B: ; 0D8B: E5 info push hl 0D8C: 219A0D ld hl, info_msg 0D8F: CD4B12 call puts 0D92: 215A01 ld hl, hello_msg 0D95: CD4B12 call puts 0D98: E1 pop hl 0D99: C9 ret 0D9A: 494E464F 0D9E: 3A2000 info_msg defb "INFO: ", eos 0DA1: ; 0DA1: ; Load data into memory. The user is prompted for a 16 bit start address. Then 0DA1: ; a sequence of bytes in hexadecimal notation may be entered until a character 0DA1: ; that is not 0-9 or a-f is encountered. 0DA1: ; 0DA1: F5 load push af 0DA2: C5 push bc 0DA3: D5 push de 0DA4: E5 push hl 0DA5: 210C0E ld hl, load_msg_1 ; Print command name 0DA8: CD4B12 call puts 0DAB: CDAF11 call get_word ; Wait for the start address (2 bytes) 0DAE: E5 push hl ; Remember address 0DAF: A7 and a ; Clear carry 0DB0: 010080 ld bc, ram_start ; Check if the address is valid 0DB3: ED42 sbc hl, bc ; by subtracting the RAM start address 0DB5: E1 pop hl ; Restore address 0DB6: 110000 ld de, 0 ; Counter for bytes loaded 0DB9: 3008 jr nc, load_loop ; OK - start reading hex characters 0DBB: E5 push hl ; Save address 0DBC: 213D0E ld hl, load_msg_3 ; Print warning message 0DBF: CD4B12 call puts 0DC2: E1 pop hl ; Restore address 0DC3: ; 0DC3: ; All in all we need two hex nibbles per byte. If two characters 0DC3: ; in a row are valid hexadecimal digits we will convert them 0DC3: ; to a byte and store this in memory. If one character is 0DC3: ; illegal, the load routine terminates and returns to the 0DC3: ; monitor. 0DC3: ; 0DC3: 3E20 load_loop ld a, ' ' 0DC5: CD4012 call putc ; Write a space as byte delimiter 0DC8: CD5911 call getc ; Read first character 0DCB: CD4311 call to_upper ; Convert to upper case 0DCE: CDF610 call is_hex ; Is it a hex digit? 0DD1: 3026 jr nc, load_exit ; No - exit the load routine 0DD3: CD1311 call nibble2val ; Convert character to value 0DD6: CD2312 call print_nibble ; Echo hex digit 0DD9: CB07 rlc a 0DDB: CB07 rlc a 0DDD: CB07 rlc a 0DDF: CB07 rlc a 0DE1: 47 ld b, a ; Save the upper four bits for later 0DE2: CD5911 call getc ; Read second character and proceed... 0DE5: CD4311 call to_upper ; Convert to upper case 0DE8: CDF610 call is_hex 0DEB: 300C jr nc, load_exit 0DED: CD1311 call nibble2val 0DF0: CD2312 call print_nibble 0DF3: B0 or b ; Combine lower 4 bits with upper 0DF4: 77 ld (hl), a ; Save value to memory 0DF5: 23 inc hl 0DF6: 13 inc de 0DF7: 18CA jr load_loop ; Get next byte (or at least try to) 0DF9: CD4C11 load_exit call crlf ; Finished... 0DFC: 626B ld hl, de ; Print number of bytes loaded 0DFE: CD3312 call print_word 0E01: 212C0E ld hl, load_msg_2 0E04: CD4B12 call puts 0E07: E1 pop hl 0E08: D1 pop de 0E09: C1 pop bc 0E0A: F1 pop af 0E0B: C9 ret 0E0C: 4C4F4144 0E10: 20287878 0E14: 206F7220 0E18: 656C7365 0E1C: 20746F20 0E20: 656E6429 0E24: 3A204144 0E28: 44523D00 load_msg_1 defb "LOAD (xx or else to end): ADDR=", eos 0E2C: 20627974 0E30: 6573206C 0E34: 6F616465 0E38: 642E0D0A 0E3C: 00 load_msg_2 defb " bytes loaded.", cr, lf, eos 0E3D: 0D0A5772 0E41: 69746520 0E45: 746F206C 0E49: 6F776572 0E4D: 206D656D 0E51: 6F727920 0E55: 62616E6B 0E59: 2C207072 0E5D: 6F626162 0E61: 6C792052 0E65: 4F4D3A20 load_msg_3 defb cr, lf, "Write to lower memory bank, probably ROM: " 0E69: 00 defb eos 0E6A: ; 0E6A: ; 0E6A: ; Load and run an executable. 0E6A: ; 0E6A: F5 load_and_run push af 0E6B: C5 push bc 0E6C: D5 push de 0E6D: E5 push hl 0E6E: FDE5 push iy 0E70: 21B20E ld hl, lar_msg_1 0E73: CD4B12 call puts ; Prompt for the filename 0E76: 216DFB ld hl, string_81_bfr 0E79: 0651 ld b, 81 ; Buffer length 0E7B: CDBA11 call gets ; Read filename 0E7E: CD5B12 call stroup ; Convert to upper case 0E81: FD21BEFB ld iy, fcb ; Prepare file open operation 0E85: 1161FB ld de, string_12_bfr 0E88: CD1C17 call fopen ; Open the file 0E8B: 210080 ld hl, ram_start ; The program is loaded into RAM 0E8E: 110000 ld de, 0 ; Counter for the number of bytes read 0E91: CD6114 lar_loop call fgetc ; Get one byte from the file 0E94: 3805 jr c, lar_exit ; EOF reached? 0E96: 77 ld (hl), a ; Save one byte into RAM 0E97: 23 inc hl ; Increment address pointer 0E98: 13 inc de ; Increment counter of bytes loaded 0E99: 18F6 jr lar_loop ; Get next byte 0E9B: CD4C11 lar_exit call crlf 0E9E: 626B ld hl, de ; How many bytes were read? 0EA0: CD3312 call print_word 0EA3: 21C10E ld hl, lar_msg_2 0EA6: CD4B12 call puts 0EA9: FDE1 pop iy 0EAB: E1 pop hl 0EAC: D1 pop de 0EAD: C1 pop bc 0EAE: F1 pop af 0EAF: C30080 jp ram_start ; Start the executable 0EB2: 52554E3A 0EB6: 2046494C 0EBA: 454E414D 0EBE: 453D00 lar_msg_1 defb "RUN: FILENAME=", eos 0EC1: 20627974 0EC5: 6573206C 0EC9: 6F616465 0ECD: 642E2052 0ED1: 756E2E2E 0ED5: 2E0D0A0D 0ED9: 0A00 lar_msg_2 defb " bytes loaded. Run...", cr, lf, cr, lf, eos 0EDB: ; 0EDB: ; Load a file's contents into memory: 0EDB: ; 0EDB: F5 load_file push af 0EDC: C5 push bc 0EDD: D5 push de 0EDE: E5 push hl 0EDF: FDE5 push iy 0EE1: 213D0F ld hl, load_file_msg_1 0EE4: CD4B12 call puts ; Print first prompt (start address) 0EE7: CDAF11 call get_word ; Wait for the start address (2 bytes) 0EEA: 225FFB ld (load_file_scrat), hl 0EED: A7 and a ; Clear carry 0EEE: 010080 ld bc, ram_start ; Check if the address is valid 0EF1: ED42 sbc hl, bc ; by subtracting the RAM start address 0EF3: 3008 jr nc, load_file_1 0EF5: 214E0F ld hl, load_file_msg_2 0EF8: CD4B12 call puts 0EFB: 182B jr load_file_exit ; Illegal address - exit routine 0EFD: 21710F load_file_1 ld hl, load_file_msg_4 0F00: CD4B12 call puts ; Prompt for filename 0F03: 216DFB ld hl, string_81_bfr 0F06: 0651 ld b, 81 ; Buffer length 0F08: CDBA11 call gets ; Read file name into bfr 0F0B: CD5B12 call stroup ; Convert to upper case 0F0E: FD21BEFB ld iy, fcb ; Prepare open (only one FCB currently) 0F12: 1161FB ld de, string_12_bfr 0F15: CD1C17 call fopen ; Open the file (if possible) 0F18: 2A5FFB ld hl, (load_file_scrat) 0F1B: 110000 ld de, 0 ; Counter for bytes loaded 0F1E: CD6114 load_file_loop call fgetc ; Get one byte from the file 0F21: 3805 jr c, load_file_exit 0F23: 77 ld (hl), a ; Store byte and 0F24: 23 inc hl ; increment pointer 0F25: 13 inc de 0F26: 18F6 jr load_file_loop ; Process next byte 0F28: CD4C11 load_file_exit call crlf 0F2B: 626B ld hl, de ; Print number of bytes loaded 0F2D: CD3312 call print_word 0F30: 21600F ld hl, load_file_msg_3 0F33: CD4B12 call puts 0F36: FDE1 pop iy 0F38: E1 pop hl 0F39: D1 pop de 0F3A: C1 pop bc 0F3B: F1 pop af 0F3C: C9 ret 0F3D: 4C4F4144 0F41: 2046494C 0F45: 453A2041 0F49: 4444523D 0F4D: 00 load_file_msg_1 defb "LOAD FILE: ADDR=", eos 0F4E: 20496C6C 0F52: 6567616C 0F56: 20616464 0F5A: 72657373 0F5E: 2100 load_file_msg_2 defb " Illegal address!", eos 0F60: 20627974 0F64: 6573206C 0F68: 6F616465 0F6C: 642E0D0A 0F70: 00 load_file_msg_3 defb " bytes loaded.", cr, lf, eos 0F71: 2046494C 0F75: 454E414D 0F79: 453D00 load_file_msg_4 defb " FILENAME=", eos 0F7C: ; 0F7C: ; mount - a wrapper for fatmount (necessary for printing the command's name) 0F7C: ; 0F7C: E5 mount push hl 0F7D: 21880F ld hl, mount_msg 0F80: CD4B12 call puts 0F83: CD4A1A call fatmount 0F86: E1 pop hl 0F87: C9 ret 0F88: 4D4F554E 0F8C: 540D0A0D 0F90: 0A00 mount_msg defb "MOUNT", cr, lf, cr, lf, eos 0F92: ; 0F92: ; Move a memory block - the user is prompted for all necessary data: 0F92: ; 0F92: F5 move push af ; We won't even destroy the flags! 0F93: C5 push bc 0F94: D5 push de 0F95: E5 push hl 0F96: 21D20F ld hl, move_msg_1 0F99: CD4B12 call puts 0F9C: CDAF11 call get_word ; Get address of block to be moved 0F9F: E5 push hl ; Push this address 0FA0: 21DE0F ld hl, move_msg_2 0FA3: CD4B12 call puts 0FA6: CDAF11 call get_word ; Get destination start address 0FA9: 545D ld de, hl ; LDIR requires this in DE 0FAB: ; Is the destination address in RAM area? 0FAB: A7 and a ; Clear carry 0FAC: 010080 ld bc, ram_start 0FAF: ED42 sbc hl, bc ; Is the destination in RAM? 0FB1: 3009 jr nc, move_get_length 0FB3: 21EC0F ld hl, move_msg_4 ; No - print error message 0FB6: CD4B12 call puts 0FB9: E1 pop hl ; Clean up stack 0FBA: 180E jr move_exit 0FBC: 21E30F move_get_length ld hl, move_msg_3 0FBF: CD4B12 call puts 0FC2: CDAF11 call get_word ; Get length of block 0FC5: 444D ld bc, hl ; LDIR requires the length in BC 0FC7: E1 pop hl ; Get address of block to be moved 0FC8: ; I was lazy - there is no test to make sure that the block 0FC8: ; to be moved will fit into the RAM area. 0FC8: EDB0 ldir ; Move block 0FCA: CD4C11 move_exit call crlf ; Finished 0FCD: E1 pop hl ; Restore registers 0FCE: D1 pop de 0FCF: C1 pop bc 0FD0: F1 pop af 0FD1: C9 ret 0FD2: 4D4F5645 0FD6: 3A204652 0FDA: 4F4D3D00 move_msg_1 defb "MOVE: FROM=", eos 0FDE: 20544F3D 0FE2: 00 move_msg_2 defb " TO=", eos 0FE3: 204C454E 0FE7: 4754483D 0FEB: 00 move_msg_3 defb " LENGTH=", eos 0FEC: 20496C6C 0FF0: 6567616C 0FF4: 20646573 0FF8: 74696E61 0FFC: 74696F6E 1000: 20616464 1004: 72657373 1008: 2100 move_msg_4 defb " Illegal destination address!", eos 100A: ; 100A: ; Dump the contents of both register banks: 100A: ; 100A: F5 rdump push af 100B: E5 push hl 100C: 215110 ld hl, rdump_msg_1 ; Print first two lines 100F: CD4B12 call puts 1012: E1 pop hl 1013: CD8210 call rdump_one_set 1016: D9 exx 1017: 08 ex af, af' 1018: E5 push hl 1019: 216810 ld hl, rdump_msg_2 101C: CD4B12 call puts 101F: E1 pop hl 1020: CD8210 call rdump_one_set 1023: 08 ex af, af' 1024: D9 exx 1025: E5 push hl 1026: 216E10 ld hl, rdump_msg_3 1029: CD4B12 call puts 102C: DDE5 push ix 102E: E1 pop hl 102F: CD3312 call print_word 1032: 217810 ld hl, rdump_msg_4 1035: CD4B12 call puts 1038: FDE5 push iy 103A: E1 pop hl 103B: CD3312 call print_word 103E: 217D10 ld hl, rdump_msg_5 1041: CD4B12 call puts 1044: 210000 ld hl, 0 1047: 39 add hl, sp 1048: CD3312 call print_word 104B: CD4C11 call crlf 104E: E1 pop hl 104F: F1 pop af 1050: C9 ret 1051: 52454749 1055: 53544552 1059: 2044554D 105D: 500D0A0D 1061: 0A093173 1065: 743A00 rdump_msg_1 defb "REGISTER DUMP", cr, lf, cr, lf, tab, "1st:", eos 1068: 09326E64 106C: 3A00 rdump_msg_2 defb tab, "2nd:", eos 106E: 09505452 1072: 3A204958 1076: 3D00 rdump_msg_3 defb tab, "PTR: IX=", eos 1078: 2049593D 107C: 00 rdump_msg_4 defb " IY=", eos 107D: 2053503D 1081: 00 rdump_msg_5 defb " SP=", eos 1082: ; 1082: E5 rdump_one_set push hl ; Print one register set 1083: 21B210 ld hl, rdump_os_msg_1 1086: CD4B12 call puts 1089: F5 push af ; Move AF into HL 108A: E1 pop hl 108B: CD3312 call print_word ; Print contents of AF 108E: 21B710 ld hl, rdump_os_msg_2 1091: CD4B12 call puts 1094: 6069 ld hl, bc 1096: CD3312 call print_word ; Print contents of BC 1099: 21BC10 ld hl, rdump_os_msg_3 109C: CD4B12 call puts 109F: 626B ld hl, de 10A1: CD3312 call print_word ; Print contents of DE 10A4: 21C110 ld hl, rdump_os_msg_4 10A7: CD4B12 call puts 10AA: E1 pop hl ; Restore original HL 10AB: CD3312 call print_word ; Print contents of HL 10AE: CD4C11 call crlf 10B1: C9 ret 10B2: 2041463D 10B6: 00 rdump_os_msg_1 defb " AF=", eos 10B7: 2042433D 10BB: 00 rdump_os_msg_2 defb " BC=", eos 10BC: 2044453D 10C0: 00 rdump_os_msg_3 defb " DE=", eos 10C1: 20484C3D 10C5: 00 rdump_os_msg_4 defb " HL=", eos 10C6: ; 10C6: #if N8VEM = 1 10C6: ; 10C6: ; rom2ram copies the ROM contents to upper RAM, switches the lower 32 kB memory 10C6: ; bank to RAM, and copies the ROM contents back. This functionality is quite 10C6: ; handy to perform patches to the monitor without having to burn an EPROM. 10C6: ; 10C6: rom2ram push hl 10C6: ld hl, rom2ram_m1 10C6: call puts 10C6: ld hl, rom_start ; Copy from this address 10C6: ld de, ram_start ; to this address 10C6: ld bc, end_of_monitor - rom_start + 1 10C6: ldir ; Copy BC bytes 10C6: ; Now we have a copy of this monitor in the upper 32 KB 10C6: ; RAM bank. We will now jump into this copy to perform the 10C6: ; second copy step without crashing: 10C6: jp rom2ram_switch + ram_start 10C6: rom2ram_switch ld a, $8f ; Select highest RAM bank 10C6: out (mpcl_ram), a ; for lower 32 kB of memory 10C6: ld a, $80 ; Disable ROM, enable RAM 10C6: out (mpcl_rom), a ; Switch it! 10C6: ld hl, ram_start ; Prepare second copy step - source, 10C6: ld de, rom_start ; destination, length remains the same 10C6: ldir ; Copy block 10C6: ld hl, rom2ram_m2 ; Print completion message 10C6: call puts 10C6: pop hl 10C6: ret 10C6: rom2ram_m1 defb "ROM2RAM", cr, lf, eos 10C6: rom2ram_m2 defb tab, "The monitor is now running in lower RAM." 10C6: defb cr, lf, eos 10C6: ; 10C6: #endif 10C6: ; 10C6: ; Start a program - this will prompt for a four digital hexadecimal start 10C6: ; address. A program should end with "jp $0" to enter the monitor again. 10C6: ; 10C6: 21D310 start ld hl, start_msg 10C9: CD4B12 call puts 10CC: CDAF11 call get_word ; Wait for a four-nibble address 10CF: CD4C11 call crlf 10D2: E9 jp (hl) ; Start program (and hope for the best) 10D3: 53544152 10D7: 543A2041 10DB: 4444523D 10DF: 00 start_msg defb "START: ADDR=", eos 10E0: ; 10E0: ; 10E0: ; unmount - simple wrapper for fatunmount (necessary for printing the command 10E0: ; name) 10E0: ; 10E0: E5 unmount push hl 10E1: 21EC10 ld hl, unmount_msg 10E4: CD4B12 call puts 10E7: CD421D call fatunmount 10EA: E1 pop hl 10EB: C9 ret 10EC: 554E4D4F 10F0: 554E540D 10F4: 0A00 unmount_msg defb "UNMOUNT", cr, lf, eos 10F6: ; 10F6: ;****************************************************************************** 10F6: ;*** 10F6: ;*** String routines 10F6: ;*** 10F6: ;****************************************************************************** 10F6: ; 10F6: ; is_hex checks a character stored in A for being a valid hexadecimal digit. 10F6: ; A valid hexadecimal digit is denoted by a set C flag. 10F6: ; 10F6: FE47 is_hex cp 'F' + 1 ; Greater than 'F'? 10F8: D0 ret nc ; Yes 10F9: FE30 cp '0' ; Less than '0'? 10FB: 3002 jr nc, is_hex_1 ; No, continue 10FD: 3F ccf ; Complement carry (i.e. clear it) 10FE: C9 ret 10FF: FE3A is_hex_1 cp '9' + 1 ; Less or equal '9*? 1101: D8 ret c ; Yes 1102: FE41 cp 'A' ; Less than 'A'? 1104: 3002 jr nc, is_hex_2 ; No, continue 1106: 3F ccf ; Yes - clear carry and return 1107: C9 ret 1108: 37 is_hex_2 scf ; Set carry 1109: C9 ret 110A: ; 110A: ; is_print checks if a character is a printable ASCII character. A valid 110A: ; character is denoted by a set C flag. 110A: ; 110A: FE20 is_print cp space 110C: 3002 jr nc, is_print_1 110E: 3F ccf 110F: C9 ret 1110: FE7F is_print_1 cp $7f 1112: C9 ret 1113: ; 1113: ; nibble2val expects a hexadecimal digit (upper case!) in A and returns the 1113: ; corresponding value in A. 1113: ; 1113: FE3A nibble2val cp '9' + 1 ; Is it a digit (less or equal '9')? 1115: 3802 jr c, nibble2val_1 ; Yes 1117: D607 sub 7 ; Adjust for A-F 1119: D630 nibble2val_1 sub '0' ; Fold back to 0..15 111B: E60F and $f ; Only return lower 4 bits 111D: C9 ret 111E: ; 111E: ; strchr: HL points to the string to be searched, A contains the desired 111E: ; character. On return HL contains the address of the position where 111E: ; the character was found. If carry is set upon return the character 111E: ; was found. 111E: ; 111E: C5 strchr push bc 111F: 47 ld b, a ; Remember character 1120: 7E strchr_loop ld a, (hl) ; Compare one character 1121: FE00 cp 0 ; Not really necessary... 1123: 2806 jr z, strchr_not ; Terminating 0 found? 1125: B8 cp b ; Is it the one we are lkg. for? 1126: 2806 jr z, strchr_found ; Yes! 1128: 23 inc hl ; Increment pointer 1129: 18F5 jr strchr_loop ; Cmp. next character 112B: A7 strchr_not and a ; Reset carry flag 112C: 1801 jr strchr_exit 112E: 37 strchr_found scf ; Set carry 112F: C1 strchr_exit pop bc 1130: C9 ret 1131: ; 1131: ; Compare two null terminated strings, return >0 / 0 / <0 in A, works like 1131: ; strcmp. The routine expects two pointer in HL and DE which will be 1131: ; preserved. 1131: ; 1131: D5 strcmp push de 1132: E5 push hl 1133: 1A strcmp_loop ld a, (de) 1134: FE00 cp 0 ; End of first string reached? 1136: 2807 jr z, strcmp_exit 1138: BE cp (hl) ; Compare two characters 1139: 2004 jr nz, strcmp_exit ; Different -> exit 113B: 23 inc hl ; Prepare comparing the next 113C: 13 inc de ; characters 113D: 18F4 jr strcmp_loop 113F: 96 strcmp_exit sub (hl) 1140: E1 pop hl 1141: D1 pop de 1142: C9 ret 1143: ; 1143: ; Convert a single character contained in A to upper case: 1143: ; 1143: FE61 to_upper cp 'a' ; Nothing to do if not lower case 1145: D8 ret c 1146: FE7B cp 'z' + 1 ; > 'z'? 1148: D0 ret nc ; Nothing to do, either 1149: E65F and $5f ; Convert to upper case 114B: C9 ret 114C: ; 114C: ;****************************************************************************** 114C: ;*** 114C: ;*** IO routines 114C: ;*** 114C: ;****************************************************************************** 114C: ; 114C: ; Send a CR/LF pair: 114C: ; 114C: F5 crlf push af 114D: 3E0D ld a, cr 114F: CD4012 call putc 1152: 3E0A ld a, lf 1154: CD4012 call putc 1157: F1 pop af 1158: C9 ret 1159: ; 1159: ; Read a single character from the serial line, result is in A. This is a 1159: ; blocking system call - it will wait for a character to read! If a non 1159: ; blocking getc is needed, call getc_nowait instead. 1159: ; 1159: DB05 getc in a, (uart_register_5) 115B: CB47 bit 0, a 115D: 28FA jr z, getc ; Wait until there is a character 115F: DB00 getc_nowait in a, (uart_register_0) 1161: FE19 cp ctrl_y ; Was it a CTRL-Y? 1163: CA6711 jp z, getc_ctrl_y ; Yes, reset stack pointer and 1166: C9 ret 1167: 313BFB getc_ctrl_y ld sp, start_type - $1 116A: 217311 ld hl, ctrl_y_msg ; HL is no longer needed since we will 116D: ; reenter the main loop 116D: CD4B12 call puts 1170: C35500 jp main_loop ; reenter the monitor without 1173: ; printing a welcome message 1173: 0D0A092A 1177: 2A2A2049 117B: 4E544552 117F: 52555054 1183: 202A2A2A 1187: 0D0A00 ctrl_y_msg defb cr, lf, tab, "*** INTERRUPT ***", cr, lf, eos 118A: ; 118A: ; Get a byte in hexadecimal notation. The result is returned in A. Since 118A: ; the routine get_nibble is used only valid characters are accepted - the 118A: ; input routine only accepts characters 0-9a-f. 118A: ; 118A: C5 get_byte push bc ; Save contents of B (and C) 118B: CD9D11 call get_nibble ; Get upper nibble 118E: CB07 rlc a 1190: CB07 rlc a 1192: CB07 rlc a 1194: CB07 rlc a 1196: 47 ld b, a ; Save upper four bits 1197: CD9D11 call get_nibble ; Get lower nibble 119A: B0 or b ; Combine both nibbles 119B: C1 pop bc ; Restore B (and C) 119C: C9 ret 119D: ; 119D: ; Get a hexadecimal digit from the serial line. This routine blocks until 119D: ; a valid character (0-9a-f) has been entered. A valid digit will be echoed 119D: ; to the serial line interface. The lower 4 bits of A contain the value of 119D: ; that particular digit. 119D: ; 119D: CD5911 get_nibble call getc ; Read a character 11A0: CD4311 call to_upper ; Convert to upper case 11A3: CDF610 call is_hex ; Was it a hex digit? 11A6: 30F5 jr nc, get_nibble ; No, get another character 11A8: CD1311 call nibble2val ; Convert nibble to value 11AB: CD2312 call print_nibble 11AE: C9 ret 11AF: ; 11AF: ; Get a word (16 bit) in hexadecimal notation. The result is returned in HL. 11AF: ; Since the routines get_byte and therefore get_nibble are called, only valid 11AF: ; characters (0-9a-f) are accepted. 11AF: ; 11AF: F5 get_word push af 11B0: CD8A11 call get_byte ; Get the upper byte 11B3: 67 ld h, a 11B4: CD8A11 call get_byte ; Get the lower byte 11B7: 6F ld l, a 11B8: F1 pop af 11B9: C9 ret 11BA: ; 11BA: ; Read a string from STDIN - HL contains the buffer start address, 11BA: ; B contains the buffer length. This routine handles the backspace character 11BA: ; correctly and will echo deleted characters enclosed in backspaces (as it 11BA: ; was customary with real Teletypes used as terminals). If there are more 11BA: ; backspaces entered than there are characters to be deleted, a BEL character 11BA: ; is sent and no further action is taken. 11BA: ; 11BA: F5 gets push af 11BB: C5 push bc 11BC: D5 push de 11BD: E5 push hl 11BE: 0E00 ld c, 0 ; Input mode 11C0: 545D ld de, hl ; Remember start of buffer 11C2: CD5911 gets_loop call getc ; Get a single character 11C5: FE0D cp cr ; Skip CR characters 11C7: 28F9 jr z, gets_loop ; only LF will terminate input 11C9: FE08 cp bs ; Is it a backspace? 11CB: 2021 jr nz, gets_1 ; No 11CD: 79 ld a, c ; In which mode are we? 11CE: FE00 cp 0 11D0: 2007 jr nz, gets_bs ; Already in backspace 11D2: 0E01 ld c, 1 ; First time in backspace mode 11D4: 3E2F ld a, '/' ; Print a slash 11D6: CD4012 call putc 11D9: E5 gets_bs push hl ; Remember HL (needed soon) 11DA: A7 and a ; Clear carry 11DB: ED52 sbc hl, de ; Are we at the buffer start? 11DD: E1 pop hl ; Restore HL 11DE: 2007 jr nz, gets_del ; Not at start, delete char. 11E0: 3E07 ld a, bel ; Too many backspaces, 11E2: CD4012 call putc ; ring the bell 11E5: 18DB jr gets_loop ; and read the next character 11E7: 2B gets_del dec hl ; Delete one character 11E8: 7E ld a, (hl) ; Get character from buffer 11E9: CD4012 call putc ; and echo it 11EC: 18D4 jr gets_loop ; Read next character 11EE: F5 gets_1 push af ; Remember character 11EF: 79 ld a, c ; Did we come from backspace? 11F0: FE00 cp 0 11F2: 2807 jr z, gets_2 ; No 11F4: 0E00 ld c, 0 ; Yes - reset mode 11F6: 3E5C ld a, '\' ; Print backslash 11F8: CD4012 call putc 11FB: F1 gets_2 pop af ; Restore character 11FC: CD4012 call putc ; Echo character 11FF: FE0A cp lf ; Terminate string at 1201: 2808 jr z, gets_exit ; LF or 1203: FE0D cp cr ; CR? 1205: 2804 jr z, gets_exit 1207: 77 ld (hl), a ; Copy character to buffer 1208: 23 inc hl 1209: 10B7 djnz gets_loop 120B: 3600 gets_exit ld (hl), 0 ; Insert termination byte 120D: E1 pop hl 120E: D1 pop de 120F: C1 pop bc 1210: F1 pop af 1211: C9 ret 1212: ; 1212: ; print_byte prints a single byte in hexadecimal notation to the serial line. 1212: ; The byte to be printed is expected to be in A. 1212: ; 1212: F5 print_byte push af ; Save the contents of the registers 1213: C5 push bc 1214: 47 ld b, a 1215: 0F rrca 1216: 0F rrca 1217: 0F rrca 1218: 0F rrca 1219: CD2312 call print_nibble ; Print high nibble 121C: 78 ld a, b 121D: CD2312 call print_nibble ; Print low nibble 1220: C1 pop bc ; Restore original register contents 1221: F1 pop af 1222: C9 ret 1223: ; 1223: ; print_nibble prints a single hex nibble which is contained in the lower 1223: ; four bits of A: 1223: ; 1223: F5 print_nibble push af ; We won't destroy the contents of A 1224: E60F and $f ; Just in case... 1226: C630 add '0' ; If we have a digit we are done here. 1228: FE3A cp '9' + 1 ; Is the result > 9? 122A: 3802 jr c, print_nibble_1 122C: C607 add 'A' - '0' - $a ; Take care of A-F 122E: CD4012 print_nibble_1 call putc ; Print the nibble and 1231: F1 pop af ; restore the original value of A 1232: C9 ret 1233: ; 1233: ; print_word prints the four hex digits of a word to the serial line. The 1233: ; word is expected to be in HL. 1233: ; 1233: E5 print_word push hl 1234: F5 push af 1235: 7C ld a, h 1236: CD1212 call print_byte 1239: 7D ld a, l 123A: CD1212 call print_byte 123D: F1 pop af 123E: E1 pop hl 123F: C9 ret 1240: ; 1240: ; Send a single character to the serial line (a contains the character): 1240: ; 1240: F5 putc push af 1241: DB05 tx_ready_loop in a, (uart_register_5) 1243: CB6F bit 5, a 1245: 28FA jr z, tx_ready_loop 1247: F1 pop af 1248: D300 out (uart_register_0), a 124A: C9 ret 124B: ; 124B: ; Send a string to the serial line, HL contains the pointer to the string: 124B: ; 124B: F5 puts push af 124C: E5 push hl 124D: 7E puts_loop ld a, (hl) 124E: FE00 cp eos ; End of string reached? 1250: 2806 jr z, puts_end ; Yes 1252: CD4012 call putc 1255: 23 inc hl ; Increment character pointer 1256: 18F5 jr puts_loop ; Transmit next character 1258: E1 puts_end pop hl 1259: F1 pop af 125A: C9 ret 125B: ; 125B: ; stroup converts a string to upper case 125B: ; 125B: F5 stroup push af 125C: E5 push hl 125D: 7E stroup_loop ld a, (hl) ; Get a character 125E: FE00 cp 0 ; End of string reached? 1260: 2807 jr z, stroup_exit ; Yes 1262: CD4311 call to_upper ; No, convert to upper case 1265: 77 ld (hl), a ; Write the character back to memory 1266: 23 inc hl ; Prepare for next character 1267: 18F4 jr stroup_loop 1269: E1 stroup_exit pop hl 126A: F1 pop af 126B: C9 ret 126C: ; 126C: ; Test the UART status, RX status -> carry flag, TX status -> Z flag 126C: ; C == 1: A character is available in the buffer. 126C: ; Z == 1: A character can be sent. 126C: ; 126C: DB05 uart_status in a, (uart_register_5) 126E: 0F rrca ; Rotate RX status into carry 126F: CB67 bit 4, a ; Check TX status (after rot!) 1271: C9 ret 1272: ; 1272: ; 1272: ;****************************************************************************** 1272: ;*** 1272: ;*** IDE routines 1272: ;*** 1272: ;****************************************************************************** 1272: ; 1272: ; Miscellaneous contants: 1272: ; 1272: ide_retries equ $ff 1272: ; 1272: #if N8VEM = 1 1272: ; 1272: ; Control bytes for setting up the 82C55 for reading/writing from/to an 1272: ; IDE device: 1272: ; 1272: ppide_rd equ $92 ; CTL -> out, LSB/MSB <- in 1272: ppide_wr equ $80 ; CTL/LSB/MSB -> out 1272: ; 1272: ; Constants for IDE control lines: 1272: ; 1272: line_ide_a0 equ $01 1272: line_ide_a1 equ $02 1272: line_ide_a2 equ $04 1272: line_ide_cs0 equ $08 1272: line_ide_cs1 equ $10 1272: line_ide_wr equ $20 1272: line_ide_rd equ $40 1272: line_ide_rst equ $80 1272: ; 1272: ; Combined constants for various IDE-registers: 1272: ; 1272: reg_ide_data equ line_ide_cs0 1272: reg_ide_err equ line_ide_cs0 + line_ide_a0 1272: ; 1272: ; Bit mapping of ide_error_code register: 1272: ; 1272: ; 0: 1 = DAM not found 1272: ; 1: 1 = Track 0 not found 1272: ; 2: 1 = Command aborted 1272: ; 3: Reserved 1272: ; 4: 1 = ID not found 1272: ; 5: Reserved 1272: ; 6: 1 = Uncorrectable ECC error 1272: ; 7: 1 = Bad block detected 1272: ; 1272: reg_ide_secnum equ line_ide_cs0 + line_ide_a1 1272: ; 1272: ; Typically set to 1 sector to be transferred 1272: ; 1272: reg_ide_lba0 equ line_ide_cs0 + line_ide_a1 + line_ide_a0 1272: reg_ide_lba1 equ line_ide_cs0 + line_ide_a2 1272: reg_ide_lba2 equ line_ide_cs0 + line_ide_a2 + line_ide_a0 1272: reg_ide_lba3 equ line_ide_cs0 + line_ide_a2 + line_ide_a1 1272: ; 1272: ; Bit mapping of ide_lba3 register: 1272: ; 1272: ; 0 - 3: LBA bits 24 - 27 1272: ; 4 : Master (0) or slave (1) selection 1272: ; 5 : Always 1 1272: ; 6 : Set to 1 for LBA access 1272: ; 7 : Always 1 1272: ; 1272: reg_ide_cmd equ line_ide_cs0 + line_ide_a2 + line_ide_a1 + line_ide_a0 1272: reg_ide_status equ reg_ide_cmd 1272: ; 1272: ; Useful commands (when written): 1272: ; 1272: ; $20: Read sectors with retry 1272: ; $30: Write sectors with retry 1272: ; $EC: Identify drive 1272: ; 1272: ; Status bits (when read): 1272: ; 1272: ; 0 = ERR: 1 = Previous command resulted in an error 1272: ; 1 = IDX: Unused 1272: ; 2 = CORR: Unused 1272: ; 3 = DRQ: 1 = Data Request Ready (sector buffer ready) 1272: ; 4 = DSC: Unused 1272: ; 5 = DF: 1 = Write fault 1272: ; 6 = RDY: 1 = Ready to accept command 1272: ; 7 = BUSY: 1 = Controller is busy executing a command 1272: ; 1272: reg_ide_cntl equ line_ide_cs1 + line_ide_a2 + line_ide_a1 1272: reg_ide_astatus equ line_ide_cs1 + line_ide_a2 + line_ide_a1 + line_ide_a0 1272: ; 1272: ; IDE commands: 1272: ; 1272: ide_cmd_recal equ $10 1272: ide_cmd_read equ $20 1272: ide_cmd_write equ $30 1272: ide_cmd_init equ $91 1272: ide_cmd_id equ $ec 1272: ide_cmd_down equ $e0 1272: ide_cmd_spinup equ $e1 1272: ; 1272: ; IDE routines: 1272: ; 1272: ; Test if the buffer of the IDE disk drive is ready for transfer. If not, 1272: ; carry will be set, otherwise carry is reset. The contents of register A will 1272: ; be destroyed! 1272: ; 1272: ide_bfr_ready push bc 1272: and a ; Clear carry assuming no error 1272: ld b, ide_retries ; How many retries? 1272: ide_bfr_loop ld a, reg_ide_status ; Prepare reading the IDE SR 1272: call ppide_read ; Read status register 1272: ld a, c ; Get lower 8 bits 1272: bit 3, a ; Check DRQ bit 1272: jr nz, ide_bfr_exit ; Buffer is ready 1272: push bc 1272: ld b, $0 ; Wait a moment 1272: ide_bfr_wait nop 1272: djnz ide_bfr_wait 1272: pop bc 1272: djnz ide_bfr_loop ; Retry 1272: scf ; Set carry to indicate timeout 1272: ld hl, ide_bfr_rdy_err 1272: call puts 1272: ide_bfr_exit pop bc 1272: ret 1272: ide_bfr_rdy_err defb "FATAL(IDE): ide_bfr_ready timeout!", cr, lf, eos 1272: ; 1272: ; Test if there is any error flagged by the drive. If carry is cleared, no 1272: ; error occured, otherwise carry will be set. The contents of register A will 1272: ; be destroyed. 1272: ; 1272: ide_error_check and a ; Clear carry (no err expected) 1272: ld a, reg_ide_status ; Prepare reading the IDE SR 1272: call ppide_read ; Read the status register 1272: ld a, c ; Get lower 8 bits 1272: bit 0, a ; Test error bit 1272: jr z, ide_ec_exit ; Everything is OK 1272: scf ; Set carry due to error 1272: ide_ec_exit ret 1272: ; 1272: ; Get ID information from drive. HL is expected to point to a 512 byte byte 1272: ; sector buffer. If carry is set, the function did not complete correctly and 1272: ; was aborted. 1272: ; 1272: ide_get_id push af 1272: push bc 1272: push hl 1272: call ide_ready ; Is the drive ready? 1272: jr c, ide_get_id_err ; No - timeout! 1272: ld c, $a0 ; Master, no LBA addressing 1272: ld a, reg_ide_lba3 ; Prepare writing LBA3 1272: call ppide_write ; Perform write access 1272: call ide_ready ; Did the command complete? 1272: jr c, ide_get_id_err ; Timeout! 1272: ld c, $ec ; Command to read ID 1272: ld a, reg_ide_cmd ; Prepare writing CMD register 1272: call ppide_write 1272: call ide_ready ; Can we proceed? 1272: jr c, ide_get_id_err ; No - timeout, propagate carry 1272: call ide_error_check ; Any errors? 1272: jr c, ide_get_id_err ; Yes - something went wrong 1272: call ide_bfr_ready ; Is the buffer ready to read? 1272: jr c, ide_get_id_err ; No 1272: ld hl, buffer ; Load the buffer's address 1272: ld b, $0 ; We will read 256 words 1272: ide_get_id_lp push bc ; PPIDE routines destroy BC! 1272: ld a, reg_ide_data ; Read 16 bits of data 1272: call ppide_read ; into BC (MSB/LSB) 1272: ld (hl), b ; Store high byte 1272: inc hl ; Increment address pointer 1272: ld (hl), c ; Store low byte 1272: inc hl ; Increment address pointer 1272: pop bc ; Restore BC (loop counter) 1272: djnz ide_get_id_lp ; Read next word 1272: jr ide_get_id_exit ; Everything OK, just exit 1272: ide_get_id_err ld hl, ide_get_id_msg ; Print error message 1272: call puts 1272: ide_get_id_exit pop hl 1272: pop bc 1272: pop af 1272: ret 1272: ide_get_id_msg defb "FATAL(IDE): Aborted!", cr, lf 1272: ; 1272: ; Test if the IDE drive is not busy and ready to accept a command. If it is 1272: ; ready the carry flag will be reset and the function returns. If a time out 1272: ; occurs, C will be set prior to returning to the caller. Register A will 1272: ; be destroyed! 1272: ; 1272: ide_ready push bc 1272: and a ; Clear carry assuming no error 1272: ld b, ide_retries ; Number of retries to timeout 1272: ide_ready_loop ld a, reg_ide_status ; Prepare reading the IDE SR 1272: call ppide_read ; Read status register 1272: ld a, c ; Get lower 8 bits 1272: and a, $c0 ; Only bits 7 and 6 are needed 1272: xor $40 ; Invert the ready flag 1272: jr z, ide_ready_exit ; Exit if ready and not busy 1272: push bc 1272: ld b, $0 ; Wait a moment 1272: ide_ready_wait nop 1272: djnz ide_ready_wait 1272: pop bc 1272: djnz ide_ready_loop ; Retry 1272: scf ; Set carry due to timeout 1272: ld hl, ide_rdy_error 1272: call puts 1272: ld a, reg_ide_err ; Prepare reading error code 1272: call ppide_read 1272: ld a, c 1272: call print_byte 1272: ide_ready_exit pop bc 1272: ret 1272: ide_rdy_error defb "FATAL(IDE): ide_ready timeout!", cr, lf, eos 1272: ; 1272: ; Read a sector from the drive. If carry is set after return, the function did 1272: ; not complete correctly due to a timeout. HL is expected to contain the start 1272: ; address of the sector buffer while BC and DE contain the sector address 1272: ; (LBA3, 2, 1 and 0). Register A's contents will be destroyed! 1272: ; 1272: ide_rs push bc 1272: push hl 1272: call ide_ready ; Is the drive ready? 1272: jr c, ide_rs_err ; No - timeout! 1272: call ide_set_lba ; Setup the drive's registers 1272: call ide_ready ; Everything OK? 1272: jr c, ide_rs_err ; No - timeout! 1272: ld c, ide_cmd_read ; Prepare a read command 1272: ld a, reg_ide_cmd 1272: call ppide_write ; Issue read command 1272: call ide_ready ; Can we proceed? 1272: jr c, ide_rs_err ; No - timeout, set carry 1272: call ide_error_check ; Any errors? 1272: jr c, ide_rs_err ; Yes - something went wrong 1272: call ide_bfr_ready ; Is the buffer ready to read? 1272: jr c, ide_rs_err ; No 1272: ld b, $0 ; We will read 256 words 1272: ide_rs_loop push bc ; PPIDE-routines destroy BC! 1272: ld a, reg_ide_data ; Read 16 bits of data 1272: call ppide_read ; into BC (MSB/LSB) 1272: ld (hl), c ; Store low byte 1272: inc hl ; Increment address pointer 1272: ld (hl), b ; Store high byte 1272: inc hl ; Increment pointer 1272: pop bc 1272: djnz ide_rs_loop ; Read next word until done 1272: jr ide_rs_exit 1272: ide_rs_err ld hl, ide_rs_err_msg ; Print error message 1272: call puts 1272: ide_rs_exit pop hl 1272: pop bc 1272: ret 1272: ide_rs_err_msg defb "FATAL(IDE): ide_rs timeout!", cr, lf, eos 1272: ; 1272: ; Set sector count and LBA registers of the drive. Registers BC and DE contain 1272: ; the sector address (LBA 3, 2, 1 and 0). 1272: ; 1272: ide_set_lba push af 1272: push bc ; BC will be destroyed 1272: push bc ; ...twice! 1272: ld c, $1 ; We will transfer 1272: ld a, reg_ide_secnum ; one sector at a time 1272: call ppide_write 1272: ld c, e ; Set LBA0 1272: ld a, reg_ide_lba0 1272: call ppide_write 1272: ld c, d ; Set LBA1 1272: ld a, reg_ide_lba1 1272: call ppide_write 1272: pop bc ; Restore BC to LBA2/3 1272: ld a, reg_ide_lba2 ; Set LBA2 1272: call ppide_write 1272: ld a, b ; Special treatment for LBA3 1272: and $0f ; Only bits 0 - 3 are LBA3 1272: or $e0 ; Select LBA and master drive 1272: ld c, a ; Set LBA3 1272: ld a, reg_ide_lba3 1272: call ppide_write 1272: pop bc 1272: pop af 1272: ret 1272: ; 1272: ; Write a sector to the drive. If carry is set after return, the function did 1272: ; not complete correctly due to a timeout. HL is expected to contain the start 1272: ; address of the sector buffer while BC and DE contain the sector address 1272: ; (LBA3, 2, 1 and 0). Register A's contents will be destroyed! 1272: ; 1272: ide_ws push bc 1272: push hl 1272: call ide_ready ; Is the drive ready? 1272: jr c, ide_ws_err ; No - timeout! 1272: call ide_set_lba ; Setup the drive's registers 1272: call ide_ready ; Everything OK? 1272: jr c, ide_ws_err ; No - timeout! 1272: ld c, ide_cmd_write ; Prepare write command 1272: ld a, reg_ide_cmd 1272: call ppide_write ; Execute read command 1272: call ide_ready ; Can we proceed? 1272: jr c, ide_ws_err ; No - timeout, set carry 1272: call ide_error_check ; Any errors? 1272: jr c, ide_ws_err ; Yes - something went wrong 1272: call ide_bfr_ready ; Is the buffer ready to read? 1272: jr c, ide_ws_err ; No 1272: ld b, $0 ; We will write 256 words 1272: ide_ws_loop push bc ; BC will be destroyed 1272: ld c, (hl) ; Read low byte 1272: inc hl ; Increment pointer 1272: ld b, (hl) ; Read high byte 1272: inc hl 1272: ld a, reg_ide_data ; Prepare writing to data reg. 1272: call ppide_write 1272: pop bc 1272: djnz ide_ws_loop 1272: jr ide_ws_exit 1272: ide_ws_err ld hl, ide_ws_err_msg ; Print error message 1272: call puts 1272: ide_ws_exit pop hl 1272: pop bc 1272: ret 1272: ide_ws_err_msg defb "FATAL(IDE): ide_ws timeout!", cr, lf, eos 1272: ; 1272: ; PPIDE-low level IO routines: 1272: ; 1272: ; ppide_read: Read from an IDE controller register. A contains the IDE 1272: ; register address while B and C will hold the 16 bit read from that 1272: ; particular register. Please note that asserting the various control and 1272: ; address lines has to be done sequentially, first asserting the address 1272: ; bits then setting read, doing the actual read access, deasserting read 1272: ; and finally clearing the address bits. A and F will not be changed. 1272: ; 1272: ; 1272: ppide_read push af ; Save register number 1272: push af ; ...twice 1272: ld a, ppide_rd ; Set PPI ports for read acc. 1272: out (reg_ppi_cntl), a ; Configure PPI 1272: pop af ; Restore register number 1272: out (reg_ppide_cntl), a ; Setup address 1272: or line_ide_rd ; Assert read signal 1272: out (reg_ppide_cntl), a ; Send address + read 1272: push af ; We will need the reg. again 1272: in a, (reg_ppide_lsb) ; Read LSB from IDE 1272: ld c, a ; Store it into C 1272: in a, (reg_ppide_msb) ; Read MSB from IDE 1272: ld b, a ; Store it into B 1272: pop af ; Prepare deassertion 1272: xor line_ide_rd ; Clear read signal 1272: out (reg_ppide_cntl), a ; Deassert read 1272: xor a ; Clear address bits, too 1272: out (reg_ppide_cntl), a 1272: pop af ; Restore original contents 1272: ret 1272: ; 1272: ; ppide_write: Perform a write access to an IDE controller register. Register 1272: ; A contains the desired register address, B and C contain the MSB/LSB of the 1272: ; value to be written to the IDE controller. A and F will not be changed. 1272: ; 1272: ppide_write push af ; Save register number 1272: push af ; ...twice 1272: ld a, ppide_wr ; Set PPI ports for write acc. 1272: out (reg_ppi_cntl), a ; Configure PPI 1272: ld a, c ; Get LSB to be written 1272: out (reg_ppide_lsb), a ; Set PPI lines 1272: ld a, b ; Get MSB 1272: out (reg_ppide_msb), a ; Set PPI lines 1272: pop af ; Restore register number 1272: out (reg_ppide_cntl), a ; Setup address lines 1272: or line_ide_wr ; Prepare write line 1272: out (reg_ppide_cntl), a ; Assert write signal 1272: xor line_ide_wr ; Reset write bit 1272: out (reg_ppide_cntl), a ; Deassert write signal 1272: xor a ; Reset address lines 1272: out (reg_ppide_cntl), a ; Deassert all signals 1272: pop af ; Restore original contents 1272: ret 1272: ; 1272: #else ; Homebrew Z80 computer 1272: ; 1272: ide_data_low equ ide_base + $0 1272: ide_data_high equ ide_base + $8 1272: ide_error_code equ ide_base + $1 1272: ; 1272: ; Bit mapping of ide_error_code register: 1272: ; 1272: ; 0: 1 = DAM not found 1272: ; 1: 1 = Track 0 not found 1272: ; 2: 1 = Command aborted 1272: ; 3: Reserved 1272: ; 4: 1 = ID not found 1272: ; 5: Reserved 1272: ; 6: 1 = Uncorrectable ECC error 1272: ; 7: 1 = Bad block detected 1272: ; 1272: ide_secnum equ ide_base + $2 1272: ; 1272: ; Typically set to 1 sector to be transf. 1272: ; 1272: ide_lba0 equ ide_base + $3 1272: ide_lba1 equ ide_base + $4 1272: ide_lba2 equ ide_base + $5 1272: ide_lba3 equ ide_base + $6 1272: ; 1272: ; Bit mapping of ide_lba3 register: 1272: ; 1272: ; 0 - 3: LBA bits 24 - 27 1272: ; 4 : Master (0) or slave (1) selection 1272: ; 5 : Always 1 1272: ; 6 : Set to 1 for LBA access 1272: ; 7 : Always 1 1272: ; 1272: ide_status_cmd equ ide_base + $7 1272: ; 1272: ; Useful commands (when written): 1272: ; 1272: ; $20: Read sectors with retry 1272: ; $30: Write sectors with retry 1272: ; $EC: Identify drive 1272: ; 1272: ; Status bits (when read): 1272: ; 1272: ; 0 = ERR: 1 = Previous command resulted in an error 1272: ; 1 = IDX: Unused 1272: ; 2 = CORR: Unused 1272: ; 3 = DRQ: 1 = Data Request Ready (sector buffer ready) 1272: ; 4 = DSC: Unused 1272: ; 5 = DF: 1 = Write fault 1272: ; 6 = RDY: 1 = Ready to accept command 1272: ; 7 = BUSY: 1 = Controller is busy executing a command 1272: ; 1272: ; Test if the buffer of the IDE disk drive is ready for transfer. If not, 1272: ; carry will be set, otherwise carry is reset. The contents of register A will 1272: ; be destroyed! 1272: ; 1272: C5 ide_bfr_ready push bc 1273: A7 and a ; Clear carry assuming no error 1274: 06FF ld b, ide_retries ; How many retries? 1276: DB17 ide_bfr_loop in a, (ide_status_cmd) ; Read IDE status register 1278: CB5F bit 3, a ; Check DRQ bit 127A: 2010 jr nz, ide_bfr_exit ; Buffer is ready 127C: C5 push bc 127D: 0600 ld b, $0 ; Wait a moment 127F: 00 ide_bfr_wait nop 1280: 10FD djnz ide_bfr_wait 1282: C1 pop bc 1283: 10F1 djnz ide_bfr_loop ; Retry 1285: 37 scf ; Set carry to indicate timeout 1286: 218E12 ld hl, ide_bfr_rdy_err 1289: CD4B12 call puts 128C: C1 ide_bfr_exit pop bc 128D: C9 ret 128E: 46415441 1292: 4C284944 1296: 45293A20 129A: 6964655F 129E: 6266725F 12A2: 72656164 12A6: 79207469 12AA: 6D656F75 12AE: 74210D0A 12B2: 00 ide_bfr_rdy_err defb "FATAL(IDE): ide_bfr_ready timeout!", cr, lf, eos 12B3: ; 12B3: ; Test if there is any error flagged by the drive. If carry is cleared, no 12B3: ; error occured, otherwise carry will be set. The contents of register A will 12B3: ; be destroyed. 12B3: ; 12B3: A7 ide_error_check and a ; Clear carry (no err expected) 12B4: DB17 in a, (ide_status_cmd) ; Read status register 12B6: CB47 bit 0, a ; Test error bit 12B8: 2801 jr z, ide_ec_exit ; Everything is OK 12BA: 37 scf ; Set carry due to error 12BB: C9 ide_ec_exit ret 12BC: ; 12BC: ; Get ID information from drive. HL is expected to point to a 512 byte byte 12BC: ; sector buffer. If carry is set, the function did not complete correctly and 12BC: ; was aborted. 12BC: ; 12BC: F5 ide_get_id push af 12BD: C5 push bc 12BE: E5 push hl 12BF: CD1213 call ide_ready ; Is the drive ready? 12C2: 382E jr c, ide_get_id_err ; No - timeout! 12C4: 3EA0 ld a, $a0 ; Master, no LBA addressing 12C6: D316 out (ide_lba3), a 12C8: CD1213 call ide_ready ; Did the command complete? 12CB: 3825 jr c, ide_get_id_err ; Timeout! 12CD: 3EEC ld a, $ec ; Command to read ID 12CF: D317 out (ide_status_cmd), a ; Write command to drive 12D1: CD1213 call ide_ready ; Can we proceed? 12D4: 381C jr c, ide_get_id_err ; No - timeout, propagate carry 12D6: CDB312 call ide_error_check ; Any errors? 12D9: 3817 jr c, ide_get_id_err ; Yes - something went wrong 12DB: CD7212 call ide_bfr_ready ; Is the buffer ready to read? 12DE: 3812 jr c, ide_get_id_err ; No 12E0: 2100FE ld hl, buffer ; Load the buffer's address 12E3: 0600 ld b, $0 ; We will read 256 words 12E5: DB10 ide_get_id_lp in a, (ide_data_low) ; Read high (!) byte 12E7: 4F ld c, a 12E8: DB18 in a, (ide_data_high) ; Read low (!) byte 12EA: 77 ld (hl), a 12EB: 23 inc hl 12EC: 71 ld (hl), c 12ED: 23 inc hl 12EE: 10F5 djnz ide_get_id_lp ; Read next word 12F0: 1806 jr ide_get_id_exit ; Everything OK, just exit 12F2: 21FC12 ide_get_id_err ld hl, ide_get_id_msg ; Print error message 12F5: CD4B12 call puts 12F8: E1 ide_get_id_exit pop hl 12F9: C1 pop bc 12FA: F1 pop af 12FB: C9 ret 12FC: 46415441 1300: 4C284944 1304: 45293A20 1308: 41626F72 130C: 74656421 1310: 0D0A ide_get_id_msg defb "FATAL(IDE): Aborted!", cr, lf 1312: ; 1312: ; Test if the IDE drive is not busy and ready to accept a command. If it is 1312: ; ready the carry flag will be reset and the function returns. If a time out 1312: ; occurs, C will be set prior to returning to the caller. Register A will 1312: ; be destroyed! 1312: ; 1312: C5 ide_ready push bc 1313: A7 and a ; Clear carry assuming no error 1314: 06FF ld b, ide_retries ; Number of retries to timeout 1316: DB17 ide_ready_loop in a, (ide_status_cmd) ; Read drive status 1318: E6C0 and a, $c0 ; Only bits 7 and 6 are needed 131A: EE40 xor $40 ; Invert the ready flag 131C: 2815 jr z, ide_ready_exit ; Exit if ready and not busy 131E: C5 push bc 131F: 0600 ld b, $0 ; Wait a moment 1321: 00 ide_ready_wait nop 1322: 10FD djnz ide_ready_wait 1324: C1 pop bc 1325: 10EF djnz ide_ready_loop ; Retry 1327: 37 scf ; Set carry due to timeout 1328: 213513 ld hl, ide_rdy_error 132B: CD4B12 call puts 132E: DB11 in a, (ide_error_code) 1330: CD1212 call print_byte 1333: C1 ide_ready_exit pop bc 1334: C9 ret 1335: 46415441 1339: 4C284944 133D: 45293A20 1341: 6964655F 1345: 72656164 1349: 79207469 134D: 6D656F75 1351: 74210D0A 1355: 00 ide_rdy_error defb "FATAL(IDE): ide_ready timeout!", cr, lf, eos 1356: ; 1356: ; Read a sector from the drive. If carry is set after return, the function did 1356: ; not complete correctly due to a timeout. HL is expected to contain the start 1356: ; address of the sector buffer while BC and DE contain the sector address 1356: ; (LBA3, 2, 1 and 0). Register A's contents will be destroyed! 1356: ; 1356: C5 ide_rs push bc 1357: E5 push hl 1358: CD1213 call ide_ready ; Is the drive ready? 135B: 3829 jr c, ide_rs_err ; No - timeout! 135D: CDAD13 call ide_set_lba ; Setup the drive's registers 1360: CD1213 call ide_ready ; Everything OK? 1363: 3821 jr c, ide_rs_err ; No - timeout! 1365: 3E20 ld a, $20 1367: D317 out (ide_status_cmd), a ; Issue read command 1369: CD1213 call ide_ready ; Can we proceed? 136C: 3818 jr c, ide_rs_err ; No - timeout, set carry 136E: CDB312 call ide_error_check ; Any errors? 1371: 3813 jr c, ide_rs_err ; Yes - something went wrong 1373: CD7212 call ide_bfr_ready ; Is the buffer ready to read? 1376: 380E jr c, ide_rs_err ; No 1378: 0600 ld b, $0 ; We will read 256 words 137A: DB10 ide_rs_loop in a, (ide_data_low) ; Read low byte 137C: 77 ld (hl), a ; Store this byte 137D: 23 inc hl 137E: DB18 in a, (ide_data_high) ; Read high byte 1380: 77 ld (hl), a 1381: 23 inc hl 1382: 10F6 djnz ide_rs_loop ; Read next word until done 1384: 1806 jr ide_rs_exit 1386: 218F13 ide_rs_err ld hl, ide_rs_err_msg ; Print error message 1389: CD4B12 call puts 138C: E1 ide_rs_exit pop hl 138D: C1 pop bc 138E: C9 ret 138F: 46415441 1393: 4C284944 1397: 45293A20 139B: 6964655F 139F: 72732074 13A3: 696D656F 13A7: 7574210D 13AB: 0A00 ide_rs_err_msg defb "FATAL(IDE): ide_rs timeout!", cr, lf, eos 13AD: ; 13AD: ; Set sector count and LBA registers of the drive. Registers BC and DE contain 13AD: ; the sector address (LBA 3, 2, 1 and 0). 13AD: ; 13AD: F5 ide_set_lba push af 13AE: 3E01 ld a, $1 ; We will transfer 13B0: D312 out (ide_secnum), a ; one sector at a time 13B2: 7B ld a, e 13B3: D313 out (ide_lba0), a ; Set LBA0, 1 and 2 directly 13B5: 7A ld a, d 13B6: D314 out (ide_lba1), a 13B8: 79 ld a, c 13B9: D315 out (ide_lba2), a 13BB: 78 ld a, b ; Special treatment for LBA3 13BC: E60F and $0f ; Only bits 0 - 3 are LBA3 13BE: F6E0 or $e0 ; Select LBA and master drive 13C0: D316 out (ide_lba3), a 13C2: F1 pop af 13C3: C9 ret 13C4: ; 13C4: ; Write a sector from the drive. If carry is set after return, the function did 13C4: ; not complete correctly due to a timeout. HL is expected to contain the start 13C4: ; address of the sector buffer while BC and DE contain the sector address 13C4: ; (LBA3, 2, 1 and 0). Register A's contents will be destroyed! 13C4: ; 13C4: C5 ide_ws push bc 13C5: E5 push hl 13C6: CD1213 call ide_ready ; Is the drive ready? 13C9: 382A jr c, ide_ws_err ; No - timeout! 13CB: CDAD13 call ide_set_lba ; Setup the drive's registers 13CE: CD1213 call ide_ready ; Everything OK? 13D1: 3822 jr c, ide_ws_err ; No - timeout! 13D3: 3E30 ld a, $30 13D5: D317 out (ide_status_cmd), a ; Issue read command 13D7: CD1213 call ide_ready ; Can we proceed? 13DA: 3819 jr c, ide_ws_err ; No - timeout, set carry 13DC: CDB312 call ide_error_check ; Any errors? 13DF: 3814 jr c, ide_ws_err ; Yes - something went wrong 13E1: CD7212 call ide_bfr_ready ; Is the buffer ready to read? 13E4: 380F jr c, ide_ws_err ; No 13E6: 0600 ld b, $0 ; We will write 256 word 13E8: 7E ide_ws_loop ld a, (hl) ; Get first byte from memory 13E9: 4F ld c, a 13EA: 23 inc hl 13EB: 7E ld a, (hl) ; Get next byte 13EC: D318 out (ide_data_high), a ; Write high byte to controller 13EE: 79 ld a, c ; Recall low byte again 13EF: D310 out (ide_data_low), a ; Write low byte -> strobe 13F1: 10F5 djnz ide_ws_loop 13F3: 1806 jr ide_ws_exit 13F5: 21FE13 ide_ws_err ld hl, ide_ws_err_msg ; Print error message 13F8: CD4B12 call puts 13FB: E1 ide_ws_exit pop hl 13FC: C1 pop bc 13FD: C9 ret 13FE: 46415441 1402: 4C284944 1406: 45293A20 140A: 6964655F 140E: 77732074 1412: 696D656F 1416: 7574210D 141A: 0A00 ide_ws_err_msg defb "FATAL(IDE): ide_ws timeout!", cr, lf, eos 141C: ; 141C: #endif ; N8VEM = 1? 141C: ; 141C: ;****************************************************************************** 141C: ;*** 141C: ;*** Miscellaneous functions 141C: ;*** 141C: ;****************************************************************************** 141C: ; 141C: ; Clear the computer (not to be called - jump into this routine): 141C: ; 141C: 213CFB cold_start ld hl, start_type 141F: 3600 ld (hl), $00 1421: 212D14 warm_start ld hl, clear_msg 1424: CD4B12 call puts 1427: 3E00 ld a, $00 1429: 32FFFF ld (ram_end), a 142C: C7 rst $00 142D: 434C4541 1431: 520D0A00 clear_msg defb "CLEAR", cr, lf, eos 1435: ; 1435: ; 1435: ;****************************************************************************** 1435: ;*** 1435: ;*** Mathematical routines 1435: ;*** 1435: ;****************************************************************************** 1435: ; 1435: ; 32 bit add routine from 1435: ; http://www.andreadrian.de/oldcpu/Z80_number_cruncher.html 1435: ; 1435: ; ADD ROUTINE 32+32BIT=32BIT 1435: ; H'L'HL = H'L'HL + D'E'DE 1435: ; CHANGES FLAGS 1435: ; 1435: 19 ADD32: ADD HL,DE ; 16-BIT ADD OF HL AND DE 1436: D9 EXX 1437: ED5A ADC HL,DE ; 16-BIT ADD OF HL AND DE WITH CARRY 1439: D9 EXX 143A: C9 RET 143B: ; 143B: ; 32 bit multiplication routine from 143B: ; http://www.andreadrian.de/oldcpu/Z80_number_cruncher.html 143B: ; 143B: ; MULTIPLY ROUTINE 32*32BIT=32BIT 143B: ; H'L'HL = B'C'BC * D'E'DE; NEEDS REGISTER A, CHANGES FLAGS 143B: ; 143B: A7 MUL32: AND A ; RESET CARRY FLAG 143C: ED62 SBC HL,HL ; LOWER RESULT = 0 143E: D9 EXX 143F: ED62 SBC HL,HL ; HIGHER RESULT = 0 1441: 78 LD A,B ; MPR IS AC'BC 1442: 0620 LD B,32 ; INITIALIZE LOOP COUNTER 1444: MUL32LOOP: 1444: CB2F SRA A ; RIGHT SHIFT MPR 1446: CB19 RR C 1448: D9 EXX 1449: CB18 RR B 144B: CB19 RR C ; LOWEST BIT INTO CARRY 144D: 3005 JR NC,MUL32NOADD 144F: 19 ADD HL,DE ; RESULT += MPD 1450: D9 EXX 1451: ED5A ADC HL,DE 1453: D9 EXX 1454: MUL32NOADD: 1454: CB23 SLA E ; LEFT SHIFT MPD 1456: CB12 RL D 1458: D9 EXX 1459: CB13 RL E 145B: CB12 RL D 145D: 10E5 DJNZ MUL32LOOP 145F: D9 EXX 1460: C9 RET 1461: ; 1461: ;****************************************************************************** 1461: ;*** 1461: ;*** FAT file system routines 1461: ;*** 1461: ;****************************************************************************** 1461: ; 1461: ; Read a single byte from a file. IY points to the FCB. The byte read is 1461: ; returned in A, on EOF the carry flag will be set. 1461: ; 1461: C5 fgetc push bc 1462: D5 push de 1463: E5 push hl 1464: ; Check if fcb_file_pointer == fcb_file_size. In this case we have reached 1464: ; EOF and will return with a set carry bit. (As a side effect, the attempt to 1464: ; read from a file which has not been successfully opened before will be 1464: ; handled like encountering an EOF at the first fgetc call.) 1464: FD7E0C ld a, (iy + fcb_file_size) 1467: FDBE13 cp (iy + fcb_file_pointer) 146A: 201C jr nz, fgetc_start 146C: FD7E0D ld a, (iy + fcb_file_size + 1) 146F: FDBE14 cp (iy + fcb_file_pointer + 1) 1472: 2014 jr nz, fgetc_start 1474: FD7E0E ld a, (iy + fcb_file_size + 2) 1477: FDBE15 cp (iy + fcb_file_pointer + 2) 147A: 200C jr nz, fgetc_start 147C: FD7E0F ld a, (iy + fcb_file_size + 3) 147F: FDBE16 cp (iy + fcb_file_pointer + 3) 1482: 2004 jr nz, fgetc_start 1484: ; We have reached EOF, so set carry and leave this routine: 1484: 37 scf 1485: C37115 jp fgetc_exit 1488: ; Check if the lower 9 bits of the file pointer are zero. In this case 1488: ; we need to read another sector (maybe from another cluster): 1488: FD7E13 fgetc_start ld a, (iy + fcb_file_pointer) 148B: FE00 cp 0 148D: C23D15 jp nz, fgetc_getc ; Bits 0-7 are not zero 1490: FD7E14 ld a, (iy + fcb_file_pointer + 1) 1493: E601 and 1 1495: C23D15 jp nz, fgetc_getc ; Bit 8 is not zero 1498: ; The file_pointer modulo 512 is zero, so we have to load the next sector: 1498: ; We have to check if fcb_current_cluster == 0 which will be the case in the 1498: ; initial run. Then we will copy fcb_first_cluster into fcb_current_cluster. 1498: FD7E17 ld a, (iy + fcb_current_cluster) 149B: FE00 cp 0 149D: 2015 jr nz, fgetc_continue ; Not the initial case 149F: FD7E18 ld a, (iy + fcb_current_cluster + 1) 14A2: FE00 cp 0 14A4: 200E jr nz, fgetc_continue ; Not the initial case 14A6: ; Initial case: We have to fill fcb_current_cluster with fcb_first_cluste: 14A6: FD7E10 ld a, (iy + fcb_first_cluster) 14A9: FD7717 ld (iy + fcb_current_cluster), a 14AC: FD7E11 ld a, (iy + fcb_first_cluster + 1) 14AF: FD7718 ld (iy + fcb_current_cluster + 1), a 14B2: 1837 jr fgetc_clu2sec 14B4: ; Here is the normal case - we will check if fcb_cluster_sector is zero - 14B4: ; in this case we have to determine the next sector to be loaded by looking 14B4: ; up the FAT. Otherwise (fcb_cluster_sector != 0) we will just get the next 14B4: ; sector in the current cluster. 14B4: FD7E1D fgetc_continue ld a, (iy + fcb_cluster_sector) 14B7: 2043 jr nz, fgetc_same ; The current cluster is valid 14B9: ; Here we know that we need the first sector of the next cluster of the file. 14B9: ; The upper eight bits of the fcb_current_cluster point to the sector of the 14B9: ; FAT where the entry we are looking for is located (this is true since a 14B9: ; sector contains 512 bytes which corresponds to 256 FAT entries). So we must 14B9: ; load the sector with the number fatstart + fcb_current_cluster[15-8] into 14B9: ; the IDE buffer and locate the entry with the address 14B9: ; fcb_current_cluster[7-0] * 2. This entry contains the sector number we are 14B9: ; looking for. 14B9: 2AF4FD ld hl, (fat1start) 14BC: FD4E18 ld c, (iy + fcb_current_cluster + 1) 14BF: 0600 ld b, 0 14C1: 09 add hl, bc 14C2: 545D ld de, hl ; Needed for ide_rs 14C4: 010000 ld bc, 0 14C7: 2AF6FD ld hl, (fat1start + 2) 14CA: ED4A adc hl, bc 14CC: 444D ld bc, hl ; Needed for ide_rs 14CE: 2100FE ld hl, buffer 14D1: CD5613 call ide_rs 14D4: ; Now the sector containing the FAT entry we are looking for is available in 14D4: ; the IDE buffer. Now we need fcb_current_cluster[7-0] * 2 14D4: 0600 ld b, 0 14D6: FD4E17 ld c, (iy + fcb_current_cluster) 14D9: CB21 sla c 14DB: CB10 rl b 14DD: ; Now get the entry: 14DD: 2100FE ld hl, buffer 14E0: 09 add hl, bc 14E1: 4E23462B ld bc, (hl) 14E5: FD7117 ld (iy + fcb_current_cluster), c 14E8: FD7017 ld (iy + fcb_current_cluster), b 14EB: ; Now we determine the first sector of the cluster to be read: 14EB: 3AE5FD fgetc_clu2sec ld a, (clusiz) ; Initialize fcb_cluster_sector 14EE: FD771D ld (iy + fcb_cluster_sector), a 14F1: FD6E17 ld l, (iy + fcb_current_cluster) 14F4: FD6618 ld h, (iy + fcb_current_cluster + 1) 14F7: CD3818 call clu2sec ; Convert cluster to sector 14FA: 1826 jr fgetc_rs 14FC: A7 fgetc_same and a ; Clear carry 14FD: 010100 ld bc, 1 ; Increment fcb_current_sector 1500: FD6E19 ld l, (iy + fcb_current_sector) 1503: FD661A ld h, (iy + fcb_current_sector + 1) 1506: 09 add hl, bc 1507: FD7519 ld (iy + fcb_current_sector), l 150A: 5D ld e, l ; Needed for ide_rs 150B: FD741A ld (iy + fcb_current_sector + 1), h 150E: 54 ld d, h ; Needed for ide_rs 150F: FD6E1B ld l, (iy + fcb_current_sector + 2) 1512: FD661C ld h, (iy + fcb_current_sector + 3) 1515: 010000 ld bc, 0 1518: ED4A adc hl, bc 151A: FD751B ld (iy + fcb_current_sector + 2), l 151D: 4D ld c, l ; Needed for ide_rs 151E: FD741C ld (iy + fcb_current_sector + 3), h 1521: 44 ld b, h ; Neede for ide_rs 1522: FD7319 fgetc_rs ld (iy + fcb_current_sector), e ; Now read the sector 1525: FD721A ld (iy + fcb_current_sector + 1), d 1528: FD711B ld (iy + fcb_current_sector + 2), c 152B: FD701C ld (iy + fcb_current_sector + 3), b 152E: ; Let HL point to the sector buffer in the FCB: 152E: FDE5 push iy ; Start of FCB 1530: E1 pop hl 1531: C5 push bc 1532: 011E00 ld bc, fcb_file_buffer ; Displacement of sector buffer 1535: 09 add hl, bc 1536: C1 pop bc 1537: CD5613 call ide_rs ; Read a single sector from disk 153A: ; Since we have read a sector we have to decrement fcb_cluster_sector 153A: FD351D dec (iy + fcb_cluster_sector) 153D: ; Here we read and return a single character from the sector buffer: 153D: FDE5 fgetc_getc push iy 153F: E1 pop hl ; Copy IY to HL 1540: 011E00 ld bc, fcb_file_buffer 1543: 09 add hl, bc ; HL points to the sector bfr. 1544: ; Get the lower 9 bits of the file pointer as displacement for the buffer: 1544: FD4E13 ld c, (iy + fcb_file_pointer) 1547: FD7E14 ld a, (iy + fcb_file_pointer + 1) 154A: E601 and 1 ; Get rid of bits 9-15 154C: 47 ld b, a 154D: 09 add hl, bc ; Add byte offset 154E: 7E ld a, (hl) ; get one byte from buffer 154F: ; Increment the file pointer: 154F: FD6E13 ld l, (iy + fcb_file_pointer) 1552: FD6614 ld h, (iy + fcb_file_pointer + 1) 1555: 010100 ld bc, 1 1558: 09 add hl, bc 1559: FD7513 ld (iy + fcb_file_pointer), l 155C: FD7414 ld (iy + fcb_file_pointer + 1), h 155F: 010000 ld bc, 0 1562: FD6E15 ld l, (iy + fcb_file_pointer + 2) 1565: FD6616 ld h, (iy + fcb_file_pointer + 3) 1568: ED4A adc hl, bc 156A: FD7515 ld (iy + fcb_file_pointer + 2), l 156D: FD7416 ld (iy + fcb_file_pointer + 3), h 1570: ; 1570: A7 and a ; Clear carry 1571: E1 fgetc_exit pop hl 1572: D1 pop de 1573: C1 pop bc 1574: C9 ret 1575: ; 1575: ; Clear the FCB to which IY points -- this should be called every time one 1575: ; creates a new FCB. (Please note that fopen does its own call to clear_fcb.) 1575: ; 1575: F5 clear_fcb push af ; We have to save so many 1576: C5 push bc ; Registers since the FCB is 1577: D5 push de ; cleared using LDIR. 1578: E5 push hl 1579: 3E00 ld a, 0 157B: FDE5 push iy 157D: E1 pop hl 157E: 77 ld (hl), a ; Clear first byte of FCB 157F: 545D ld de, hl 1581: 13 inc de 1582: 011E00 ld bc, fcb_file_buffer 1585: EDB0 ldir ; And transfer this zero byte 1587: E1 pop hl ; down to the relevant rest 1588: D1 pop de ; of the buffer. 1589: C1 pop bc 158A: F1 pop af 158B: C9 ret 158C: ; 158C: ; Dump a file control block (FCB) - the start address is expected in IY. 158C: ; 158C: F5 dump_fcb push af 158D: E5 push hl 158E: 211E16 ld hl, dump_fcb_1 1591: CD4B12 call puts 1594: FDE5 push iy ; Load HL with 1596: E1 pop hl ; the contents of IY 1597: CD3312 call print_word 159A: ; Print the filename: 159A: 213716 ld hl, dump_fcb_2 159D: CD4B12 call puts 15A0: FDE5 push iy 15A2: E1 pop hl 15A3: CD4B12 call puts 15A6: ; Print file size: 15A6: 214C16 ld hl, dump_fcb_3 15A9: CD4B12 call puts 15AC: FD660F ld h, (iy + fcb_file_size + 3) 15AF: FD6E0E ld l, (iy + fcb_file_size + 2) 15B2: CD3312 call print_word 15B5: FD660D ld h, (iy + fcb_file_size + 1) 15B8: FD6E0C ld l, (iy + fcb_file_size) 15BB: CD3312 call print_word 15BE: ; Print cluster number: 15BE: 216116 ld hl, dump_fcb_4 15C1: CD4B12 call puts 15C4: FD6611 ld h, (iy + fcb_first_cluster + 1) 15C7: FD6E10 ld l, (iy + fcb_first_cluster) 15CA: CD3312 call print_word 15CD: ; Print file type: 15CD: 217616 ld hl, dump_fcb_5 15D0: CD4B12 call puts 15D3: FD7E12 ld a, (iy + fcb_file_type) 15D6: CD1212 call print_byte 15D9: ; Print file pointer: 15D9: 218B16 ld hl, dump_fcb_6 15DC: CD4B12 call puts 15DF: FD6616 ld h, (iy + fcb_file_pointer + 3) 15E2: FD6E15 ld l, (iy + fcb_file_pointer + 2) 15E5: CD3312 call print_word 15E8: FD6614 ld h, (iy + fcb_file_pointer + 1) 15EB: FD6E13 ld l, (iy + fcb_file_pointer) 15EE: CD3312 call print_word 15F1: ; Print current cluster number: 15F1: 21A016 ld hl, dump_fcb_7 15F4: CD4B12 call puts 15F7: FD6618 ld h, (iy + fcb_current_cluster + 1) 15FA: FD6E17 ld l, (iy + fcb_current_cluster) 15FD: CD3312 call print_word 1600: ; Print current sector: 1600: 21B516 ld hl, dump_fcb_8 1603: CD4B12 call puts 1606: FD661C ld h, (iy + fcb_current_sector + 3) 1609: FD6E1B ld l, (iy + fcb_current_sector + 2) 160C: CD3312 call print_word 160F: FD661A ld h, (iy + fcb_current_sector + 1) 1612: FD6E19 ld l, (iy + fcb_current_sector) 1615: CD3312 call print_word 1618: CD4C11 call crlf 161B: E1 pop hl 161C: F1 pop af 161D: C9 ret 161E: 44756D70 1622: 206F6620 1626: 46434220 162A: 61742061 162E: 64647265 1632: 73733A20 1636: 00 dump_fcb_1 defb "Dump of FCB at address: ", eos 1637: 0D0A0946 163B: 696C6520 163F: 6E616D65 1643: 20202020 1647: 20203A20 164B: 00 dump_fcb_2 defb cr, lf, tab, "File name : ", eos 164C: 0D0A0946 1650: 696C6520 1654: 73697A65 1658: 20202020 165C: 20203A20 1660: 00 dump_fcb_3 defb cr, lf, tab, "File size : ", eos 1661: 0D0A0931 1665: 73742063 1669: 6C757374 166D: 65722020 1671: 20203A20 1675: 00 dump_fcb_4 defb cr, lf, tab, "1st cluster : ", eos 1676: 0D0A0946 167A: 696C6520 167E: 74797065 1682: 20202020 1686: 20203A20 168A: 00 dump_fcb_5 defb cr, lf, tab, "File type : ", eos 168B: 0D0A0946 168F: 696C6520 1693: 706F696E 1697: 74657220 169B: 20203A20 169F: 00 dump_fcb_6 defb cr, lf, tab, "File pointer : ", eos 16A0: 0D0A0943 16A4: 75727265 16A8: 6E742063 16AC: 6C757374 16B0: 65723A20 16B4: 00 dump_fcb_7 defb cr, lf, tab, "Current cluster: ", eos 16B5: 0D0A0943 16B9: 75727265 16BD: 6E742073 16C1: 6563746F 16C5: 72203A20 16C9: 00 dump_fcb_8 defb cr, lf, tab, "Current sector : ", eos 16CA: ; 16CA: ; Convert a user specified filename to an 8.3-filename without dot and 16CA: ; with terminating null byte. HL points to the input string, DE points to 16CA: ; a 12 character buffer for the filename. This function is used by 16CA: ; fopen which expects a human readable string that will be transformed into 16CA: ; an 8.3-filename without the dot for the following directory lookup. 16CA: ; 16CA: F5 str2filename push af 16CB: C5 push bc 16CC: D5 push de 16CD: E5 push hl 16CE: ED535DFB ld (str2filename_de), de 16D2: 3E20 ld a, ' ' ; Initialize output buffer 16D4: 060B ld b, $b ; Fill 11 bytes with spaces 16D6: 12 str2filiniloop ld (de), a 16D7: 13 inc de 16D8: 10FC djnz str2filiniloop 16DA: 3E00 ld a, 0 ; Add terminating null byte 16DC: 12 ld (de), a 16DD: ED5B5DFB ld de, (str2filename_de) ; Restore DE pointer 16E1: ; Start string conversion 16E1: 0608 ld b, 8 16E3: 7E str2filini_nam ld a, (hl) 16E4: FE00 cp 0 ; End of string reached? 16E6: 282F jr z, str2filini_x 16E8: FE2E cp '.' ; Dot found? 16EA: 2812 jr z, str2filini_ext 16EC: 12 ld (de), a 16ED: 13 inc de 16EE: 23 inc hl 16EF: 05 dec b 16F0: 20F1 jr nz, str2filini_nam 16F2: 7E str2filini_skip ld a, (hl) 16F3: FE00 cp 0 ; End of string without dot? 16F5: 2820 jr z, str2filini_x ; Nothing more to do 16F7: FE2E cp '.' 16F9: 2803 jr z, str2filini_ext ; Take care of extension 16FB: 23 inc hl ; Prepare for next character 16FC: 18F4 jr str2filini_skip ; Skip more characters 16FE: 23 str2filini_ext inc hl ; Skip the dot 16FF: E5 push hl ; Make sure DE points 1700: 2A5DFB ld hl, (str2filename_de) ; into the filename buffer 1703: 010800 ld bc, 8 ; at the start position 1706: 09 add hl, bc ; of the filename extension 1707: 545D ld de, hl 1709: E1 pop hl 170A: 0603 ld b, 3 170C: 7E str2filini_elp ld a, (hl) 170D: FE00 cp 0 ; End of string reached? 170F: 2806 jr z, str2filini_x ; Nothing more to do 1711: 12 ld (de), a 1712: 13 inc de 1713: 23 inc hl 1714: 05 dec b 1715: 20F5 jr nz, str2filini_elp ; Next extension character 1717: E1 str2filini_x pop hl 1718: D1 pop de 1719: C1 pop bc 171A: F1 pop af 171B: C9 ret 171C: ; 171C: ; Open a file with given filename (format: 'FFFFFFFFXXX') in the root directory 171C: ; and return the 1st cluster number for that file. If the file can not 171C: ; be found, $0000 will be returned in the FCB. 171C: ; At entry, HL must point to the string buffer while IY points to a valid 171C: ; file control block that will hold all necessary data for future file accesses. 171C: ; In addition to that DE must point to a 12 character string buffer. 171C: ; 171C: F5 fopen push af 171D: C5 push bc 171E: D5 push de 171F: E5 push hl 1720: DDE5 push ix 1722: 2255FB ld (fopen_scr), hl 1725: 21DCFD ld hl, fatname ; Check if a disk has been 1728: 7E ld a, (hl) ; mounted. 1729: FE00 cp 0 172B: CAD417 jp z, fopen_e1 ; No disk - error exit 172E: CD7515 call clear_fcb 1731: FDE5 push iy ; Copy IY to DE 1733: D1 pop de 1734: 2A55FB ld hl, (fopen_scr) ; Create the filename 1737: CDCA16 call str2filename ; Convert string to a filename 173A: 2100FE ld hl, buffer ; Compute buffer overflow 173D: 010002 ld bc, $0200 ; address - this is the bfr siz. 1740: 09 add hl, bc ; and will be used in the loop 1741: 225BFB ld (fopen_eob), hl ; This is the buffer end addr. 1744: ; 1744: 2AF8FD ld hl, (rootstart) ; Remember the initial root 1747: 2257FB ld (fopen_rsc), hl ; sector number 174A: 2AFAFD ld hl, (rootstart + 2) 174D: 2259FB ld (fopen_rsc + 2), hl 1750: ; Read one root directory sector 1750: ED4B59FB fopen_nbf ld bc, (fopen_rsc + 2) 1754: ED5B57FB ld de, (fopen_rsc) 1758: 2100FE ld hl, buffer 175B: CD5613 call ide_rs ; Read one sector 175E: DAD917 jp c, fopen_e2 ; Exit on read error 1761: 2255FB fopen_lp ld (fopen_scr), hl 1764: AF xor a ; Last entry? 1765: BE cp (hl) ; The last entry has first 1766: CADF17 jp z, fopen_x ; byte = $0 1769: 3EE5 ld a, $e5 ; Deleted entry? 176B: BE cp (hl) 176C: 284B jr z, fopen_nxt ; Get next entry 176E: ; ld (fopen_scr), hl 176E: DD2A55FB ld ix, (fopen_scr) 1772: DD7E0B ld a, (ix + $b) ; Get attribute byte 1775: FE0F cp $0f 1777: 2840 jr z, fopen_nxt ; Skip long name 1779: CB67 bit 4, a ; Skip directories 177B: 203C jr nz, fopen_nxt 177D: ; Compare the filename with the one we are looking for: 177D: DD360B00 ld (ix + $b), 0 ; Clear attribute byte 1781: ED5B55FB ld de, (fopen_scr) 1785: FDE5 push iy ; Prepare string comparison 1787: E1 pop hl 1788: CD3111 call strcmp ; Compare filename with string 178B: FE00 cp 0 ; Are strings equal? 178D: 202A jr nz, fopen_nxt ; No - check next entry 178F: DD7E1B ld a, (ix + $1a + 1) ; Read cluster number and 1792: ; Save cluster_number into fcb_first_cluster: 1792: FD7711 ld (iy + fcb_first_cluster + 1), a 1795: DD7E1A ld a, (ix + $1a) 1798: FD7710 ld (iy + fcb_first_cluster), a 179B: DD7E1C ld a, (ix + $1c) ; Save file size to FCB 179E: FD770C ld (iy + fcb_file_size), a 17A1: DD7E1D ld a, (ix + $1d) ; Save file size to FCB 17A4: FD770D ld (iy + fcb_file_size + 1), a 17A7: DD7E1E ld a, (ix + $1e) ; Save file size to FCB 17AA: FD770E ld (iy + fcb_file_size + 2), a 17AD: DD7E1F ld a, (ix + $1f) ; Save file size to FCB 17B0: FD770F ld (iy + fcb_file_size + 3), a 17B3: FD361201 ld (iy + fcb_file_type), 1 ; Set file type to found 17B7: 1826 jr fopen_x ; Terminate lookup loop 17B9: 012000 fopen_nxt ld bc, $20 17BC: 2A55FB ld hl, (fopen_scr) 17BF: 09 add hl, bc 17C0: 2255FB ld (fopen_scr), hl 17C3: ED4B5BFB ld bc, (fopen_eob) ; Check for end of buffer 17C7: A7 and a ; Clear carry 17C8: ED42 sbc hl, bc ; ...no 16 bit cp :-( 17CA: C26117 jp nz, fopen_lp ; Buffer is still valid 17CD: 2157FB ld hl, fopen_rsc ; Increment sector number 17D0: 34 inc (hl) ; 16 bits are enough :-) 17D1: C35017 jp fopen_nbf ; Read next directory sector 17D4: 21E617 fopen_e1 ld hl, fopen_nmn ; No disk mounted 17D7: 1803 jr fopen_err ; Print error message 17D9: 210718 fopen_e2 ld hl, fopen_rer ; Directoy sector read error 17DC: CD4B12 fopen_err call puts 17DF: DDE1 fopen_x pop ix 17E1: E1 pop hl 17E2: D1 pop de 17E3: C1 pop bc 17E4: F1 pop af 17E5: C9 ret 17E6: 46415441 17EA: 4C28464F 17EE: 50454E29 17F2: 3A204E6F 17F6: 20646973 17FA: 6B206D6F 17FE: 756E7465 1802: 64210D0A 1806: 00 fopen_nmn defb "FATAL(FOPEN): No disk mounted!", cr, lf, eos 1807: 46415441 180B: 4C28464F 180F: 50454E29 1813: 3A20436F 1817: 756C6420 181B: 6E6F7420 181F: 72656164 1823: 20646972 1827: 6563746F 182B: 72792073 182F: 6563746F 1833: 7221 fopen_rer defb "FATAL(FOPEN): Could not read directory sector!" 1835: 0D0A00 defb cr, lf, eos 1838: ; 1838: ; Convert a cluster number into a sector number. The cluster number is 1838: ; expected in HL, the corresponding sector number will be returned in 1838: ; BC and DE, thus ide_rs or ide_ws can be called afterwards. 1838: ; 1838: ; SECNUM = (CLUNUM - 2) * CLUSIZ + DATASTART 1838: ; 1838: F5 clu2sec push af ; Since the 32 bit 1839: E5 push hl ; multiplication routine 183A: D9 exx ; needs shadow registers 183B: C5 push bc ; we have to push many, 183C: D5 push de ; many registers here 183D: E5 push hl 183E: 010000 ld bc, 0 ; Clear BC' and DE' for 1841: 5059 ld de, bc ; 32 bit multiplication 1843: D9 exx 1844: 010200 ld bc, 2 ; Subtract 2 1847: ED42 sbc hl, bc ; HL = CLUNUM - 2 1849: 444D ld bc, hl ; BC = HL; BC' = 0 184B: 3AE5FD ld a, (clusiz) 184E: 1600 ld d, 0 ; CLUSIZ bits 8 to 15 1850: 5F ld e, a ; DE = CLUSIZ 1851: CD3B14 call MUL32 ; HL = (CLUNUM - 2) * CLUSIZ 1854: ED5BFCFD ld de, (datastart) 1858: D9 exx 1859: ED5BFEFD ld de, (datastart + 2) 185D: D9 exx 185E: CD3514 call ADD32 ; HL = HL + DATASTART 1861: D9 exx 1862: E5 push hl 1863: D9 exx 1864: C1 pop bc 1865: 545D ld de, hl 1867: D9 exx 1868: E1 pop hl 1869: D1 pop de 186A: C1 pop bc 186B: D9 exx 186C: E1 pop hl 186D: F1 pop af 186E: C9 ret 186F: ; 186F: ; Print a directory listing 186F: ; 186F: F5 dirlist push af 1870: C5 push bc 1871: D5 push de 1872: E5 push hl 1873: DDE5 push ix 1875: 21DCFD ld hl, fatname 1878: 7E ld a, (hl) 1879: FE00 cp 0 187B: CA4219 jp z, dirlist_nodisk 187E: DD216DFB ld ix, string_81_bfr 1882: DD36082E ld (ix + 8), '.' ; Dot between name and extens. 1886: DD360C00 ld (ix + 12), 0 ; String terminator 188A: 217219 ld hl, dirlist_0 ; Print title line 188D: CD4B12 call puts 1890: 2100FE ld hl, buffer ; Compute buffer overflow 1893: 010002 ld bc, $0200 ; address - this is the bfr siz. 1896: 09 add hl, bc 1897: 2251FB ld (dirlist_eob), hl ; This is the buffer end addr. 189A: ; 189A: 2AF8FD ld hl, (rootstart) ; Remember the initial root 189D: 224DFB ld (dirlist_rootsec), hl ; sector number 18A0: 2AFAFD ld hl, (rootstart + 2) 18A3: 224FFB ld (dirlist_rootsec + 2), hl 18A6: ; Read one root directory sector 18A6: ED4B4FFB dirlist_nbfr ld bc, (dirlist_rootsec + 2) 18AA: ED5B4DFB ld de, (dirlist_rootsec) 18AE: 2100FE ld hl, buffer 18B1: CD5613 call ide_rs 18B4: DA3D19 jp c, dirlist_e1 18B7: AF dirlist_loop xor a ; Last entry? 18B8: BE cp (hl) ; The last entry has first 18B9: CA4819 jp z, dirlist_exit ; byte = $0 18BC: 3EE5 ld a, $e5 ; Deleted entry? 18BE: BE cp (hl) 18BF: 2867 jr z, dirlist_next 18C1: 2253FB ld (dirlist_scratch), hl 18C4: DD2A53FB ld ix, (dirlist_scratch) 18C8: DD7E0B ld a, (ix + $b) ; Get attribute byte 18CB: FE0F cp $0f 18CD: 2859 jr z, dirlist_next ; Skip long name 18CF: 116DFB ld de, string_81_bfr ; Prepare for output 18D2: 010800 ld bc, 8 ; Copy first eight characters 18D5: EDB0 ldir 18D7: 13 inc de 18D8: 010300 ld bc, 3 ; Copy extension 18DB: EDB0 ldir 18DD: ; ld hl, de 18DD: ; ld (hl), 0 ; String terminator 18DD: 216DFB ld hl, string_81_bfr 18E0: CD4B12 call puts 18E3: 21471A ld hl, dirlist_NODIR ; Flag directories with "DIR" 18E6: CB67 bit 4, a 18E8: 2803 jr z, dirlist_prtdir 18EA: 21411A ld hl, dirlist_DIR 18ED: CD4B12 dirlist_prtdir call puts 18F0: DD661F ld h, (ix + $1c + 3) ; Get and print file size 18F3: DD6E1E ld l, (ix + $1c + 2) 18F6: CD3312 call print_word 18F9: DD661D ld h, (ix + $1c + 1) 18FC: DD6E1C ld l, (ix + $1c) 18FF: CD3312 call print_word 1902: ; Get and print start sector 1902: 3E09 ld a, tab 1904: CD4012 call putc 1907: DD661B ld h, (ix + $1a + 1) ; Get cluster number 190A: DD6E1A ld l, (ix + $1a) 190D: 010000 ld bc, 0 ; Is file empty? 1910: A7 and a ; Clear carry 1911: ED42 sbc hl, bc ; Empty file -> Z set 1913: 280D jr z, dirlist_nosize 1915: CD3818 call clu2sec 1918: 6069 ld hl, bc 191A: CD3312 call print_word 191D: 626B ld hl, de 191F: CD3312 call print_word 1922: CD4C11 dirlist_nosize call crlf 1925: 2A53FB ld hl, (dirlist_scratch) 1928: 012000 dirlist_next ld bc, $20 192B: 09 add hl, bc 192C: ED4B51FB ld bc, (dirlist_eob) ; Check for end of buffer 1930: A7 and a 1931: ED42 sbc hl, bc 1933: C2B718 jp nz, dirlist_loop ; Buffer is still valid 1936: 214DFB ld hl, dirlist_rootsec 1939: 34 inc (hl) 193A: C3A618 jp dirlist_nbfr 193D: 210F1A dirlist_e1 ld hl, dirlist_1 1940: 1803 jr dirlist_x 1942: 214F19 dirlist_nodisk ld hl, dirlist_nomnt 1945: CD4B12 dirlist_x call puts 1948: DDE1 dirlist_exit pop ix 194A: E1 pop hl 194B: D1 pop de 194C: C1 pop bc 194D: F1 pop af 194E: C9 ret 194F: 46415441 1953: 4C284449 1957: 524C4953 195B: 54293A20 195F: 4E6F2064 1963: 69736B20 1967: 6D6F756E 196B: 74656421 196F: 0D0A00 dirlist_nomnt defb "FATAL(DIRLIST): No disk mounted!", cr, lf, eos 1972: 44697265 1976: 63746F72 197A: 7920636F 197E: 6E74656E 1982: 74733A0D 1986: 0A dirlist_0 defb "Directory contents:", cr, lf 1987: 2D2D2D2D ... 19B2: 0D0A defb "-------------------------------------------", cr, lf 19B4: 46494C45 19B8: 4E414D45 19BC: 2E455854 19C0: 20204449 19C4: 523F2020 19C8: 2053495A 19CC: 45202842 19D0: 59544553 19D4: 29 defb "FILENAME.EXT DIR? SIZE (BYTES)" 19D5: 20203153 19D9: 54205345 19DD: 43540D0A defb " 1ST SECT", cr, lf 19E1: 2D2D2D2D ... 1A0C: 0D0A defb "-------------------------------------------", cr, lf 1A0E: 00 defb eos 1A0F: 46415441 1A13: 4C284449 1A17: 524C4953 1A1B: 54293A20 1A1F: 436F756C 1A23: 64206E6F 1A27: 74207265 1A2B: 61642064 1A2F: 69726563 1A33: 746F7279 1A37: 20736563 1A3B: 746F72 dirlist_1 defb "FATAL(DIRLIST): Could not read directory sector" 1A3E: 0D0A00 defb cr, lf, eos 1A41: 09444952 1A45: 0900 dirlist_DIR defb tab, "DIR", tab, eos 1A47: 090900 dirlist_NODIR defb tab, tab, eos 1A4A: ; 1A4A: ; Perform a disk mount 1A4A: ; 1A4A: F5 fatmount push af 1A4B: C5 push bc 1A4C: D5 push de 1A4D: E5 push hl 1A4E: DDE5 push ix 1A50: 2100FE ld hl, buffer ; Read MBR into buffer 1A53: 010000 ld bc, 0 1A56: 110000 ld de, 0 1A59: CD5613 call ide_rs 1A5C: DAC21B jp c, fatmount_e1 ; Error reading MBR? 1A5F: DD21FEFF ld ix, buffer + $1fe ; Check for $55AA as MBR trailer 1A63: 3E55 ld a, $55 1A65: DDBE00 cp (ix) 1A68: C2C71B jp nz, fatmount_e2 1A6B: 3EAA ld a, $aa 1A6D: DDBE01 cp (ix + 1) 1A70: C2C71B jp nz, fatmount_e2 1A73: 010800 ld bc, 8 ; Get partition start and size 1A76: 21C6FF ld hl, buffer + $1c6 1A79: 11ECFD ld de, pstart 1A7C: EDB0 ldir 1A7E: 2100FE ld hl, buffer ; Read partition boot block 1A81: ED5BECFD ld de, (pstart) 1A85: ED4BEEFD ld bc, (pstart + 2) 1A89: CD5613 call ide_rs 1A8C: DACC1B jp c, fatmount_e3 ; Error reading boot block? 1A8F: 010800 ld bc, 8 ; Copy FAT name 1A92: 2103FE ld hl, buffer + 3 1A95: 11DCFD ld de, fatname 1A98: EDB0 ldir 1A9A: DD2100FE ld ix, buffer 1A9E: 3E02 ld a, 2 ; Check for two FATs 1AA0: DDBE10 cp (ix + $10) 1AA3: C2D11B jp nz, fatmount_e4 ; Wrong number of FATs 1AA6: AF xor a ; Check for 512 bytes / sector 1AA7: DDBE0B cp (ix + $b) 1AAA: C2D61B jp nz, fatmount_e5 1AAD: 3E02 ld a, 2 1AAF: DDBE0C cp (ix + $c) 1AB2: C2D61B jp nz, fatmount_e5 1AB5: 3A0DFE ld a, (buffer + $d) ; Get cluster size 1AB8: 32E5FD ld (clusiz), a 1ABB: ED4B0EFE ld bc, (buffer + $e) ; Get reserved sector number 1ABF: ED43E6FD ld (ressec), bc 1AC3: ED4B16FE ld bc, (buffer + $16) ; Get FAT size in sectors 1AC7: ED43E8FD ld (fatsec), bc 1ACB: ED4B11FE ld bc, (buffer + $11) ; Get length of root directory 1ACF: ED43EAFD ld (rootlen), bc 1AD3: 2AECFD ld hl, (pstart) ; Compute 1AD6: ED4BE6FD ld bc, (ressec) ; FAT1START = PSTART + RESSEC 1ADA: 09 add hl, bc 1ADB: 22F4FD ld (fat1start), hl 1ADE: 2AEEFD ld hl, (pstart + 2) 1AE1: 010000 ld bc, 0 1AE4: ED4A adc hl, bc 1AE6: 22F6FD ld (fat1start + 2), hl 1AE9: 2AE8FD ld hl, (fatsec) ; Compute ROOTSTART for two FATs 1AEC: 29 add hl, hl ; ROOTSTART = FAT1START + 1AED: 444D ld bc, hl ; 2 * FATSIZ 1AEF: 2AF4FD ld hl, (fat1start) 1AF2: 09 add hl, bc 1AF3: 22F8FD ld (rootstart), hl 1AF6: 2AF6FD ld hl, (fat1start + 2) 1AF9: 010000 ld bc, 0 1AFC: ED4A adc hl, bc 1AFE: 22FAFD ld (rootstart + 2), hl 1B01: ED4BEAFD ld bc, (rootlen) ; Compute rootlen / 16 1B05: CB28 sra b ; By shifting it four places 1B07: CB19 rr c ; to the right 1B09: CB28 sra b ; This value will be used 1B0B: CB19 rr c ; for the calculation of 1B0D: CB28 sra b ; DATASTART 1B0F: CB19 rr c 1B11: CB28 sra b 1B13: CB19 rr c 1B15: 2AF8FD ld hl, (rootstart) ; Computer DATASTART 1B18: 09 add hl, bc 1B19: 22FCFD ld (datastart), hl 1B1C: 2AFAFD ld hl, (rootstart + 2) 1B1F: 010000 ld bc, 0 1B22: ED4A adc hl, bc 1B24: 22FEFD ld (datastart + 2), hl 1B27: 21C21C ld hl, fatmount_s1 ; Print mount summary 1B2A: CD4B12 call puts 1B2D: 21DCFD ld hl, fatname 1B30: CD4B12 call puts 1B33: 21CD1C ld hl, fatmount_s2 1B36: CD4B12 call puts 1B39: 3AE5FD ld a, (clusiz) 1B3C: CD1212 call print_byte 1B3F: 21D91C ld hl, fatmount_s3 1B42: CD4B12 call puts 1B45: 2AE6FD ld hl, (ressec) 1B48: CD3312 call print_word 1B4B: 21E51C ld hl, fatmount_s4 1B4E: CD4B12 call puts 1B51: 2AE8FD ld hl, (fatsec) 1B54: CD3312 call print_word 1B57: 21F11C ld hl, fatmount_s5 1B5A: CD4B12 call puts 1B5D: 2AEAFD ld hl, (rootlen) 1B60: CD3312 call print_word 1B63: 21FE1C ld hl, fatmount_s6 1B66: CD4B12 call puts 1B69: 2AF2FD ld hl, (psiz + 2) 1B6C: CD3312 call print_word 1B6F: 2AF0FD ld hl, (psiz) 1B72: CD3312 call print_word 1B75: 21091D ld hl, fatmount_s7 1B78: CD4B12 call puts 1B7B: 2AEEFD ld hl, (pstart + 2) 1B7E: CD3312 call print_word 1B81: 2AECFD ld hl, (pstart) 1B84: CD3312 call print_word 1B87: 21151D ld hl, fatmount_s8 1B8A: CD4B12 call puts 1B8D: 2AF6FD ld hl, (fat1start + 2) 1B90: CD3312 call print_word 1B93: 2AF4FD ld hl, (fat1start) 1B96: CD3312 call print_word 1B99: 21241D ld hl, fatmount_s9 1B9C: CD4B12 call puts 1B9F: 2AFAFD ld hl, (rootstart + 2) 1BA2: CD3312 call print_word 1BA5: 2AF8FD ld hl, (rootstart) 1BA8: CD3312 call print_word 1BAB: 21331D ld hl, fatmount_sa 1BAE: CD4B12 call puts 1BB1: 2AFEFD ld hl, (datastart + 2) 1BB4: CD3312 call print_word 1BB7: 2AFCFD ld hl, (datastart) 1BBA: CD3312 call print_word 1BBD: CD4C11 call crlf 1BC0: 181A jr fatmount_exit 1BC2: 21E31B fatmount_e1 ld hl, fatmount_1 1BC5: 1812 jr fatmount_x 1BC7: 210A1C fatmount_e2 ld hl, fatmount_2 1BCA: 180D jr fatmount_x 1BCC: 212A1C fatmount_e3 ld hl, fatmount_3 1BCF: 1808 jr fatmount_x 1BD1: 21611C fatmount_e4 ld hl, fatmount_4 1BD4: 1803 jr fatmount_x 1BD6: 218E1C fatmount_e5 ld hl, fatmount_5 1BD9: CD4B12 fatmount_x call puts 1BDC: DDE1 fatmount_exit pop ix 1BDE: E1 pop hl 1BDF: D1 pop de 1BE0: C1 pop bc 1BE1: F1 pop af 1BE2: C9 ret 1BE3: 46415441 1BE7: 4C284641 1BEB: 544D4F55 1BEF: 4E54293A 1BF3: 20436F75 1BF7: 6C64206E 1BFB: 6F742072 1BFF: 65616420 1C03: 4D425221 1C07: 0D0A00 fatmount_1 defb "FATAL(FATMOUNT): Could not read MBR!", cr, lf, eos 1C0A: 46415441 1C0E: 4C284641 1C12: 544D4F55 1C16: 4E54293A 1C1A: 20496C6C 1C1E: 6567616C 1C22: 204D4252 1C26: 210D0A00 fatmount_2 defb "FATAL(FATMOUNT): Illegal MBR!", cr, lf, eos 1C2A: 46415441 1C2E: 4C284641 1C32: 544D4F55 1C36: 4E54293A 1C3A: 20436F75 1C3E: 6C64206E 1C42: 6F742072 1C46: 65616420 1C4A: 70617274 1C4E: 6974696F 1C52: 6E20626F 1C56: 6F742062 1C5A: 6C6F636B fatmount_3 defb "FATAL(FATMOUNT): Could not read partition boot block" 1C5E: 0D0A00 defb cr, lf, eos 1C61: 46415441 1C65: 4C284641 1C69: 544D4F55 1C6D: 4E54293A 1C71: 20464154 1C75: 206E756D 1C79: 62657220 1C7D: 6E6F7420 1C81: 65717561 1C85: 6C207477 1C89: 6F21 fatmount_4 defb "FATAL(FATMOUNT): FAT number not equal two!" 1C8B: 0D0A00 defb cr, lf, eos 1C8E: 46415441 1C92: 4C284641 1C96: 544D4F55 1C9A: 4E54293A 1C9E: 20536563 1CA2: 746F7220 1CA6: 73697A65 1CAA: 206E6F74 1CAE: 20657175 1CB2: 616C2035 1CB6: 31322062 1CBA: 79746573 1CBE: 21 fatmount_5 defb "FATAL(FATMOUNT): Sector size not equal 512 bytes!" 1CBF: 0D0A00 defb cr, lf, eos 1CC2: 09464154 1CC6: 4E414D45 1CCA: 3A0900 fatmount_s1 defb tab, "FATNAME:", tab, eos 1CCD: 0D0A0943 1CD1: 4C555349 1CD5: 5A3A0900 fatmount_s2 defb cr, lf, tab, "CLUSIZ:", tab, eos 1CD9: 0D0A0952 1CDD: 45535345 1CE1: 433A0900 fatmount_s3 defb cr, lf, tab, "RESSEC:", tab, eos 1CE5: 0D0A0946 1CE9: 41545345 1CED: 433A0900 fatmount_s4 defb cr, lf, tab, "FATSEC:", tab, eos 1CF1: 0D0A0952 1CF5: 4F4F544C 1CF9: 454E3A09 1CFD: 00 fatmount_s5 defb cr, lf, tab, "ROOTLEN:", tab, eos 1CFE: 0D0A0950 1D02: 53495A3A 1D06: 090900 fatmount_s6 defb cr, lf, tab, "PSIZ:", tab, tab, eos 1D09: 0D0A0950 1D0D: 53544152 1D11: 543A0900 fatmount_s7 defb cr, lf, tab, "PSTART:", tab, eos 1D15: 0D0A0946 1D19: 41543153 1D1D: 54415254 1D21: 3A0900 fatmount_s8 defb cr, lf, tab, "FAT1START:", tab, eos 1D24: 0D0A0952 1D28: 4F4F5453 1D2C: 54415254 1D30: 3A0900 fatmount_s9 defb cr, lf, tab, "ROOTSTART:", tab, eos 1D33: 0D0A0944 1D37: 41544153 1D3B: 54415254 1D3F: 3A0900 fatmount_sa defb cr, lf, tab, "DATASTART:", tab, eos 1D42: ; 1D42: ; Dismount a FAT volume (invalidate the FAT control block by setting the 1D42: ; first byte (of fatname) to zero. 1D42: ; 1D42: F5 fatunmount push af 1D43: E5 push hl 1D44: AF xor a 1D45: 21DCFD ld hl, fatname 1D48: 77 ld (hl), a ; Clear first byte of fatname 1D49: E1 pop hl 1D4A: F1 pop af 1D4B: C9 ret 1D4C: ; 1D4C: ; Here the dispatch table for calling system routines starts. Every entry 1D4C: ; must contain only the destination address (2 bytes). 1D4C: ; 1D4C: 1C14 dispatch_table defw cold_start ; $00 = clear etc. 1D4E: ; Parameters: N/A 1D4E: ; Action: Performs a cold start (memory is cleared!) 1D4E: ; Return values: N/A 1D4E: ; 1D4E: F610 defw is_hex 1D50: ; Parameters: A contains a character code 1D50: ; Action: Tests ('0' <= A <= '9) || ('A' <= A <= 'F') 1D50: ; Return values: Carry bit is set if A contains a hex char. 1D50: ; 1D50: 0A11 defw is_print 1D52: ; Parameters: A contains a charater code 1D52: ; Action: Tests if the character is printable 1D52: ; Return values: Carry bit is set if A contains a valid char. 1D52: ; 1D52: 4311 defw to_upper 1D54: ; Parameters: A contains a character code 1D54: ; Action: Converts an ASCII character into upper case 1D54: ; Return values: Converted character code in A 1D54: ; 1D54: 4C11 defw crlf 1D56: ; Parameters: N/A 1D56: ; Action: Sends a CR/LF to the serial line 1D56: ; Return values: N/A 1D56: ; 1D56: 5911 defw getc 1D58: ; Parameters: N/A 1D58: ; Action: Reads a character code from the serial line 1D58: ; Return values: A contains a character code 1D58: ; 1D58: 4012 defw putc 1D5A: ; Parameters: A contains a character code 1D5A: ; Action: Sends the character code to the serial line 1D5A: ; Return values: N/A 1D5A: ; 1D5A: 4B12 defw puts 1D5C: ; Parameters: HL contains the address of a 0-terminated 1D5C: ; string 1D5C: ; Action: Send the string to the serial line (excluding 1D5C: ; the termination byte, of course) 1D5C: ; Return values: N/A 1D5C: ; 1D5C: 3111 defw strcmp 1D5E: ; Parameters: HL and DE contain the addresses of two strings 1D5E: ; Action: Compare both strings. 1D5E: ; Return values: A contains return value, <0 / 0 / >0 1D5E: ; 1D5E: BA11 defw gets 1D60: ; Parameters: HL contains a buffer address, B contains the 1D60: ; buffer length (including the terminating 1D60: ; null byte!) 1D60: ; Action: Reads a string from STDIN. Terminates when 1D60: ; either the buffer is full or the string is 1D60: ; terminated by CR/LF 1D60: ; Return values: N/A 1D60: ; 1D60: 6114 defw fgetc 1D62: ; Parameters: IY (pointer to a valid FCB) 1D62: ; Action: Reads a character from a FAT file 1D62: ; Return values: Character in A, if EOF has been encountered, 1D62: ; the carry flag will be set 1D62: ; 1D62: 8C15 defw dump_fcb 1D64: ; Parameters: IY (pointer to a valid FCB) 1D64: ; Action: Prints the contents of the FCB in human 1D64: ; readable format to STDOUT 1D64: ; Return values: N/A 1D64: ; 1D64: 1C17 defw fopen 1D66: ; Parameters: HL (points to a buffer containing the file 1D66: ; file name), IY (points to an empty FCB), 1D66: ; DE (points to a 12 character string buffer) 1D66: ; Action: Opens a file for reading 1D66: ; Return values: N/A (All information is contained in the FCB) 1D66: ; 1D66: 6F18 defw dirlist 1D68: ; Parameters: N/A (relies on a valid FAT control block) 1D68: ; Action: Writes a directory listing to STDOUT 1D68: ; Return values: N/A 1D68: ; 1D68: 4A1A defw fatmount 1D6A: ; Parameters: N/A (needs the global FAT control block) 1D6A: ; Action: Mounts a disk (populates the FAT CB) 1D6A: ; Return values: N/A 1D6A: ; 1D6A: 421D defw fatunmount 1D6C: ; Parameters: N/A (needs the global FAT control block) 1D6C: ; Action: Invalidates the global FAT control block 1D6C: ; Return values: N/A 1D6C: 1E11 defw strchr 1D6E: ; Parameters: HL contains buffer address, A contains 1D6E: ; character to be searched in buffer 1D6E: ; Action: Look for the first occurrence of a character 1D6E: ; in a string 1D6E: ; Return values: Carry bit set if character was found. HL 1D6E: ; points to the address of this character in 1D6E: ; the buffer 1D6E: 6C12 defw uart_status 1D70: ; Parameters: N/A 1D70: ; Action: Checks the UART status 1D70: ; Return values: RX status in carry flag, TX status in Z flag. 1D70: ; Register A and F are modified! 1D70: 5F11 defw getc_nowait 1D72: ; Parameters: N/A 1D72: ; Action: Reads a character code from the serial line 1D72: ; but does not wait for a character to be there. 1D72: ; This function is usable eg. for a Forth 1D72: ; interpreter etc. 1D72: ; Return values: A contains a character code 1D72: 3312 defw print_word 1D74: ; Parameters: HL contains the 16 bit value to be printed 1D74: ; Action: Prints a 16 bit value in hexadecimal repre- 1D74: ; sentation 1D74: ; Return values: N/A 1D74: 1212 defw print_byte 1D76: ; Parameters: A contains the byte to be printed 1D76: ; Action: Prints a byte in hexadecimal notation 1D76: ; Return values: N/A 1D76: 5B12 defw stroup 1D78: ; Parameters: HL points to the string to be converted 1D78: ; Action: Converts a string to upper case 1D78: ; Return values: N/A 1D78: AF11 defw get_word 1D7A: ; Parameters: N/A 1D7A: ; Action: Reads a four nibble hex-word from stdin 1D7A: ; Return values: HL contains the value read 1D7A: ; 1D7A: ;****************************************************************************** 1D7A: ;*** 1D7A: ;*** From here on various subsystems can be included (like Forth, BASIC etc.). 1D7A: ;*** 1D7A: ;****************************************************************************** 1D7A: ; 1D7A: 215334 forth_subsystem ld hl, forth_msg 1D7D: CD4B12 call puts 345B: 00 end_of_monitor defb 0 ;... ...The included CAMEL Forth interpreter resides here... ... 345C: #end ; ((inserted by zasm)) |
28-JUN-2013 |