diff --git a/.github/workflows/actions.yml b/.github/workflows/actions.yml new file mode 100644 index 0000000..abf23c3 --- /dev/null +++ b/.github/workflows/actions.yml @@ -0,0 +1,20 @@ +name: 'Quality assurance pipeline' + +on: push + +jobs: + build: + runs-on: ubuntu-22.04 + container: giomba/ceda-rom-disassembly:1 + steps: + - name: Checkout repository + uses: actions/checkout@v3 + - name: Build and test of CP/M components + run: | + make assemble + make tests + - name: Build and test of CP/M applications + run: | + cd applications + make assemble + make tests \ No newline at end of file diff --git a/Makefile b/Makefile index 6485179..eabd608 100644 --- a/Makefile +++ b/Makefile @@ -6,7 +6,7 @@ QUIET := @ ASM := cpm_bios.asm ASM += cpm_loader.asm -BIN := $(patsubst %.asm, build/%.bin, $(ASM)) +CHK_FILENAME := md5sum.txt CHK := $(patsubst %.asm, %.chk, $(ASM)) @@ -26,9 +26,9 @@ build/%.bin: %.asm $(ECHO) ' ASM $<' $(QUIET) zcc +z80 -subtype=none -o $@ $< -%.chk: %.bin build/%.bin +%.chk: build/%.bin $(ECHO) ' SUM $<' - $(QUIET) diff build/$< $< + $(QUIET) grep "`md5sum "$<"`" $(CHK_FILENAME) > /dev/null .PHONY: clean clean: diff --git a/applications/COPY8003.COM.asm b/applications/COPY8003.COM.asm new file mode 100644 index 0000000..73fc096 --- /dev/null +++ b/applications/COPY8003.COM.asm @@ -0,0 +1,324 @@ +; COPY8003, used to copy disks on a Sanco computer. +; z80asm COPY8003.COM.asm -b -o COPY8003.COM + + org $0100 + +C_WRITE := $02 +C_WRITESTR := $09 +C_READSTR := $0a + +SYSCALL := $0005 + +stack_top: + + jp entrypoint ;[0100] + +STR_WELCOME: + DB "\r\n" + DB "iBEX 8003 COPY Version 1.0" + DB "$" + +STR_INSERT: + DB "\r\n" + DB "INSERT NEW DISKETTE DRIVE B:" + DB "\r\n" + DB "THEN READY,TYPE RETURN" + DB "$" + +entrypoint: + ld sp,stack_top ;[0158] initialize stack pointer + ld de,STR_WELCOME ;[015b] print welcome message + ld c,C_WRITESTR ;[015e] + call SYSCALL ;[0160] +main_prompt: + ld de,STR_INSERT ;[0163] print insert disk message + ld c,C_WRITESTR ;[0166] + call SYSCALL ;[0168] + call BDOS_READSTR ;[016b] wait for a keypress + ld a,(read_buf+1) ;[016e] TODO don't remember how C_READSTR works + or a ;[0171] + jp z,begin_copy ;[0172] + call clear_screen ;[0175] + jp main_prompt ;[0178] + +begin_copy: + ld de,STR_NEWLINE ;[017b] + ld c,$09 ;[017e] + call SYSCALL ;[0180] + xor a ;[0183] + ld (cur_track),a ;[0184] zero current track and current side + ld (cur_side),a ;[0187] + ld a,$08 ;[018a] number of TxSx to be printed before reaching... + ld (margin_ctr),a ;[018c] ..screen border + call print_track_side ;[018f] + call copy_boot_track ;[0192] copy separately the boot track (track 0 side 0) + ld a,$04 ;[0195] force side to 1 (since track 0 side 0 was already copied) + ld (cur_side),a ;[0197] +copy_loop: + call print_track_side ;[019a] + call read_track ;[019d] + call write_track ;[01a0] + call verify_track ;[01a3] + ld a,(cur_side) ;[01a6] + bit 2,a ;[01a9] check current disk side + jr nz,next_track ;[01ab] if side 1 done, move track forward + set 2,a ;[01ad] switch side + ld (cur_side),a ;[01af] update side + jp copy_loop ;[01b2] repeat perform copy of the other side +next_track: + res 2,a ;[01b5] reset side to 0 + ld (cur_side),a ;[01b7] + ld a,(cur_track) ;[01ba] + inc a ;[01bd] move track forward + ld (cur_track),a ;[01be] update it + cp 80 ;[01c1] + jp nz,copy_loop ;[01c3] repeat loop until track 80 + jp main_prompt ;[01c6] + +read_track: + ld b,$44 ;[01c9] read, sector burst = 4 + ld a,(cur_side) ;[01cb] side + ld c,a ;[01ce] + res 0,c ;[01cf] set A: drive + ld a,(cur_track) ;[01d1] current track + ld d,a ;[01d4] + ld e,$00 ;[01d5] sector 0 + set 7,e ;[01d7] SBE enabled (read whole track) + ld hl,$1000 ;[01d9] read buffer + ld a,$03 ;[01dc] ssf = 3 (1024 bps) + call ROM_FDC_RWFS ;[01de] read track from A: drive + cp $ff ;[01e1] + ret nz ;[01e3] return on success +read_error: + ld de,STR_READERROR ;[01e4] + ld c,C_WRITESTR ;[01e7] + call SYSCALL ;[01e9] + jp exit ;[01ec] + +STR_READERROR: + DB "\r\n" + DB "READ ERROR" + DB "$" + +write_track: + ld b,$84 ;[01fc] write, sector burst = 4 + ld a,(cur_side) ;[01fe] side + ld c,a ;[0201] + set 0,c ;[0202] set B: drive + ld a,(cur_track) ;[0204] current track + ld d,a ;[0207] + ld e,$00 ;[0208] sector 0 + set 7,e ;[020a] SBE enabled (write whole track) + ld hl,$1000 ;[020c] data buffer + ld a,$03 ;[020f] ssf = 3 (1024 bps) + call ROM_FDC_RWFS ;[0211] write track to B: drive + cp $ff ;[0214] + ret nz ;[0216] return on success +write_error: + ld de,STR_WRITEERROR ;[0217] + ld c,C_WRITESTR ;[021a] + call SYSCALL ;[021c] + jp exit ;[021f] + +STR_WRITEERROR: + DB "\r\n" + DB "WRITE ERROR" + DB "$" + +verify_track: + ld b,$44 ;[0230] read, sector burst = 4 + ld a,(cur_side) ;[0232] side + ld c,a ;[0235] + set 0,c ;[0236] set B: drive + ld a,(cur_track) ;[0238] current track + ld d,a ;[023b] + ld e,$00 ;[023c] sector 0 + set 7,e ;[023e] SBE enabled (read whole track) + ld hl,$2400 ;[0240] verify buffer + ld a,$03 ;[0243] ssf = 3 (1024 bps) + call ROM_FDC_RWFS ;[0245] read track from B: drive + cp $ff ;[0248] + jp z,verify_error ;[024a] proceed with verification on success + ld hl,$1000 ;[024d] memcmp(read_buffer, write_buffer, 0x1400) + ld de,$2400 ;[0250] + ld bc,$1400 ;[0253] +verify_loop: + ld a,(de) ;[0256] + cp (hl) ;[0257] + jp nz,verify_error ;[0258] + inc hl ;[025b] + inc de ;[025c] + dec bc ;[025d] + ld a,b ;[025e] + or c ;[025f] + jp nz,verify_loop ;[0260] + ret ;[0263] +verify_error: + ld de,STR_VERIFYERROR ;[0264] + ld c,C_WRITESTR ;[0267] + call SYSCALL ;[0269] + call clear_screen ;[026c] + jp exit ;[026f] +STR_VERIFYERROR: + DB "\r\n" + DB "VERIFY ERROR" + DB "$" + +copy_boot_track: + ld b,$4f ;[0281] read, sector burst = 15 + ld c,$00 ;[0283] set a: drive + ld d,$00 ;[0285] track 0 + ld e,$00 ;[0287] sector 0 (SBE disabled?) + ld hl,$1000 ;[0289] read buffer + ld a,$01 ;[028c] ssf = 1 (256 bps) + call ROM_FDC_RWFS ;[028e] read whole track from A: drive + cp $ff ;[0291] + jp z,read_error ;[0293] + ld b,$8f ;[0296] write, sector burst = 15 + ld c,$01 ;[0298] set b: drive + ld d,$00 ;[029a] track 0 + ld e,$00 ;[029c] sector 0 (SBE disabled?) + ld hl,$1000 ;[029e] buffer to be written + ld a,$01 ;[02a1] ssf = 1 (256 bps) + call ROM_FDC_RWFS ;[02a3] write whole track to B: drive + cp $ff ;[02a6] + jp z,write_error ;[02a8] + ld b,$4f ;[02ab] read, sector burst = 15 + ld c,$01 ;[02ad] set b: drive + ld d,$00 ;[02af] track 0 + ld e,$00 ;[02b1] sector 0 (SBE disabled?) + ld hl,$2000 ;[02b3] verify buffer + ld a,$01 ;[02b6] ssf = 1 (256 bps) + call ROM_FDC_RWFS ;[02b8] read whole track from B: drive + cp $ff ;[02bb] + jp z,verify_error ;[02bd] + ld hl,$1000 ;[02c0] memcmp(read_buffer, write_buffer, 0x1000) + ld de,$2000 ;[02c3] + ld bc,$1000 ;[02c6] +verify_boot_loop: + ld a,(de) ;[02c9] + cp (hl) ;[02ca] + jp nz,verify_error ;[02cb] + inc hl ;[02ce] + inc de ;[02cf] + dec bc ;[02d0] + ld a,b ;[02d1] + or c ;[02d2] + jp nz,verify_boot_loop ;[02d3] + ret ;[02d6] + +print_track_side: + ld de,STR_TRACK ;[02d7] + ld c,C_WRITESTR ;[02da] + call SYSCALL ;[02dc] + ld a,(cur_track) ;[02df] + ld e,a ;[02e2] + call sub_0329h ;[02e3] print dec(e) + ld e,' ' ;[02e6] + call BDOS_WRITE ;[02e8] print space + ld e,'S' ;[02eb] + call BDOS_WRITE ;[02ed] print "S" + ld a,(cur_side) ;[02f0] take head currently being formatted (bit 2) + and $04 ;[02f3] + rrca ;[02f5] + rrca ;[02f6] make it a value 0/1 + or $30 ;[02f7] convert number to ASCII ('0'/'1') + ld e,a ;[02f9] + call BDOS_WRITE ;[02fa] print the side number (head) + ld a,(margin_ctr) ;[02fd] + dec a ;[0300] decrement remaining margin to screen border + ld (margin_ctr),a ;[0301] + ret nz ;[0304] + ld de,STR_NEWLINE ;[0305] add a newline once reached screen border + ld c,C_WRITESTR ;[0308] + call SYSCALL ;[030a] + ld a,$08 ;[030d] reload screen margin + ld (margin_ctr),a ;[030f] + ret ;[0312] + +STR_TRACK: + DB " T" + DB "$" + +STR_NEWLINE: + DB "\r\x00\n\x00" + DB "$" + + nop ;[031d] + +margin_ctr: + DB "\x0b" + +ROM_FDC_RWFS: + call $ffa3 ;[031f] + call $c018 ;[0322] + call $ffa6 ;[0325] + ret ;[0328] + +; TODO I imagine this is a decimal print of e +sub_0329h: + push bc ;[0329] + push de ;[032a] + ld a,e ;[032b] + ld b,$0a ;[032c] + ld c,$ff ;[032e] +l0330h: + inc c ;[0330] + sub b ;[0331] + jr nc,l0330h ;[0332] + add a,b ;[0334] TODO a = a + 9 + 8 + ... + 1 +l0335h: + ld b,a ;[0335] + ld a,c ;[0336] TODO c should be 10? + or $30 ;[0337] number to ASCII + ld e,a ;[0339] take x10 number + call BDOS_WRITE ;[033a] print ascii in e + ld a,b ;[033d] take x1 number + or $30 ;[033e] number to ASCII + ld e,a ;[0340] + call BDOS_WRITE ;[0341] + pop de ;[0344] + pop bc ;[0345] + ret ;[0346] + +BDOS_WRITE: + push af ;[0347] + push bc ;[0348] + push de ;[0349] + push hl ;[034a] + ld c,C_WRITE ;[034b] + call SYSCALL ;[034d] + pop hl ;[0350] + pop de ;[0351] + pop bc ;[0352] + pop af ;[0353] + ret ;[0354] + +BDOS_READSTR: + ld de,read_buf ;[0355] + ld c,C_READSTR ;[0358] + call SYSCALL ;[035a] + ret ;[035d] + +read_buf: + DB "\x10" + REPT 17 + DB 0 + ENDR + +clear_screen: + ld e,$07 ;[0370] + ld c,C_WRITE ;[0372] + call SYSCALL ;[0374] + ret ;[0377] + +exit: + ld c,$00 ;[0378] + jp $0005 ;[037a] + +cur_track: + DB $23 +cur_side: + DB 0 + + DB $03 diff --git a/applications/FUNK00.COM.asm b/applications/FUNK00.COM.asm new file mode 100644 index 0000000..78304f2 --- /dev/null +++ b/applications/FUNK00.COM.asm @@ -0,0 +1,1169 @@ +; FUNK00, used to remap keyboard layout on a Sanco computer. +; More information will come later, I hope. +; z80asm FUNK00.COM.asm -b -o FUNK00.COM + + org 0x0100 + +;; Functions +SYSCALL: = $0005 + +;; Constants +; The displacement of the first keymap entry inside SLF file +SLF_KEYMAP_BASE : = $1d00 +SLF_KEYMAP_OFFSET: = $0080 + +; The entrypoint checks current sanco model by reading a particular memory +; byte in CP/M BIOS area, right after service routines table. +; This "model byte" decides the keyboard peripheral address, which is stored +; in IO_KBDST,IO_KBDCH variables for later usage. + ld sp,$0ab4 ;[0100] + ld hl,($0001) ;[0103] f203 + ld de,$0030 ;[0106] + add hl,de ;[0109] -> f233 + ld a,(hl) ;[010a] On our Sanco (CEDA) this is "0" ($30) + cp $30 ;[010b] check if this Sanco is "model 0" + jr nz,model1 ;[010d] + ld a,$b3 ;[010f] Save keyboard IO address in ram + ld (IO_KBDST),a ;[0111] This is for serial status register + dec a ;[0114] + ld (IO_KBDCH),a ;[0115] The consecutive address is for data register + jr label_0135 ;[0118] from keyboard serial interface +model1: + cp $31 ;[011a] check if this Sanco is "model 1" + jr nz,incompatible ;[011c] + ld a,$d5 ;[011e] TODO im not interested since not for our sanco + ld (IO_KBDST),a ;[0120] + ld a,$d4 ;[0123] + ld (IO_KBDCH),a ;[0125] + jr label_0135 ;[0128] +incompatible: + ld de,STR_INCOMPATIBLE ;[012a] Print unsupported sanco model + ld c,$09 ;[012d] C_WRITESTR + call GUARD_SYSCALL ;[012f] + jp $0000 ;[0132] TODO just reset or go back to CP/M + +label_0135: + ld de,STR_WELCOME ;[0135] + ld c,$09 ;[0138] C_WRITESTR + call GUARD_SYSCALL ;[013a] + ld de,STR_GETFILE ;[013d] + ld c,$09 ;[0140] C_WRITESTR + call GUARD_SYSCALL ;[0142] + call CIN_FILENAME ;[0145] Get input filename + ld de,$0080 ;[0148] + ld c,$1a ;[014b] F_DMAOFF + call GUARD_SYSCALL ;[014d] + ld de,fd ;[0150] file descriptor address + ld c,$0f ;[0153] F_OPEN(fd) + call GUARD_SYSCALL ;[0155] F_OPEN returns $FF for error + inc a ;[0158] increment to get $00 on error + jp z,err_nofile ;[0159] TODO file not found error? + ld hl,slfbuff ;[015c] TODO this is the base ptr to special keys string? no, because is used as read buffer + ld (dmaoff_p),hl ;[015f] + ld b,$00 ;[0162] +fread_all: + ld de,(dmaoff_p) ;[0164] + ld c,$1a ;[0168] F_DMAOFF + call GUARD_SYSCALL ;[016a] + ld de,fd ;[016d] file descriptor address + ld c,$14 ;[0170] F_READ(fd) + call GUARD_SYSCALL ;[0172] + or a ;[0175] F_READ returns $00 if ok + jr nz,fread_eof ;[0176] TODO any error is considered as EOF? + ld hl,(dmaoff_p) ;[0178] Update DMA pointer for the next record + ld de,$0080 ;[017b] + add hl,de ;[017e] + ld (dmaoff_p),hl ;[017f] + inc b ;[0182] keep the count of the number of read records + jr fread_all ;[0183] +fread_eof: + ld a,b ;[0185] + ld (slf_block_size),a ;[0186] preserve the number of read records from the SLF file + ld de,fd ;[0189] + ld c,$10 ;[018c] F_CLOSE(fd) + call GUARD_SYSCALL ;[018e] + ld de,STR_WELCOME ;[0191] + ld c,$09 ;[0194] C_WRITESTR(STR_WELCOME) + call GUARD_SYSCALL ;[0196] + ld de,STR_NORMAL ;[0199] + ld c,$09 ;[019c] C_WRITESTR(STR_NORMAL) + call GUARD_SYSCALL ;[019e] + ld hl,slfbuff ;[01a1] TODO this is again the read buffer + ld de,SLF_KEYMAP_BASE ;[01a4] This is the offset of the first keymap table in the SLF file + add hl,de ;[01a7] + ld (layout_p),hl ;[01a8] + ld a,$00 ;[01ab] + ld (isnotplain),a ;[01ad] + call DISPLAYKBD ;[01b0] TODO print keymap on screen +remap_normal_loop: + ld de,STR_PROMPT ;[01b3] + ld c,$09 ;[01b6] C_WRITESTR + call GUARD_SYSCALL ;[01b8] + ; + di ;[01bb] Direct access to keyboard I/O, interrupts are disabled + ld a,(IO_KBDST) ;[01bc] Fetch keyboard's status register I/O address + ld c,a ;[01bf] Depending on the peripheral address (i.e. the + cp $b3 ;[01c0] model), we have a different mask to read keyboard + jr nz,lmodel1 ;[01c2] status register + ; Direct I/O access to keyboard for Sanco model "0" + ; (i.e. the one we have) + ; Read the status register until an available flag is raised + ; Here is bit 0, active high +lmodel0: + in a,(c) ;[01c4] Read keyboard status? + bit 0,a ;[01c6] TODO + jr z,lmodel0 ;[01c8] + jr L01d2 ;[01ca] + ; Direct I/O access to keyboard for Sanco model "1" + ; Read the status register until an available flag is raised + ; Here is bit 1, active high +lmodel1: + in a,(c) ;[01cc] + and $02 ;[01ce] + jr z,lmodel1 ;[01d0] +L01d2: + ld a,(IO_KBDCH) ;[01d2] Fetch keyboard's data register I/O address + ld c,a ;[01d5] + in a,(c) ;[01d6] + ld e,a ;[01d8] put the first data byte in e register + + ; The "read status - read data" code is repeated as before. + ; Data is read twice, first byte is scancode, which is kept. + ; Second byte is flags, which is discarded. + ld a,(IO_KBDST) ;[01d9] + ld c,a ;[01dc] + cp $b3 ;[01dd] + jr nz,label_01e9 ;[01df] +label_01e1: + in a,(c) ;[01e1] + bit 0,a ;[01e3] + jr z,label_01e1 ;[01e5] + jr label_01ef ;[01e7] +label_01e9: + in a,(c) ;[01e9] + and $02 ;[01eb] + jr z,label_01e9 ;[01ed] +label_01ef: + ld a,(IO_KBDCH) ;[01ef] + ld c,a ;[01f2] + in a,(c) ;[01f3] + ei ;[01f5] End of direct I/O access + + ld a,e ;[01f6] Take scancode + cp $39 ;[01f7] This is the spacebar scancode + jr z,label_0200 ;[01f9] when spacebar is pressed, break cycle + call doremap ;[01fb] TODO: remap scancode + jr remap_normal_loop ;[01fe] Repeat keboard reading +label_0200: + ld hl,(layout_p) ;[0200] Update keymap pointer to the next block + ld de,SLF_KEYMAP_OFFSET ;[0203] TODO: SHIFT layer + add hl,de ;[0206] + ld (layout_p),hl ;[0207] + ld de,STR_WELCOME ;[020a] + ld c,$09 ;[020d] C_WRITESTR + call GUARD_SYSCALL ;[020f] + ld de,STR_SHIFT ;[0212] + ld c,$09 ;[0215] C_WRITESTR + call GUARD_SYSCALL ;[0217] + ld a,$01 ;[021a] + ld (isnotplain),a ;[021c] + call DISPLAYKBD ;[021f] +remap_shift_loop: + ld de,STR_PROMPT ;[0222] + ld c,$09 ;[0225] + call GUARD_SYSCALL ;[0227] + ; The following code is same as before: catch keypress, + ; with direct I/O access, to remap scancode + di ;[022a] + ld a,(IO_KBDST) ;[022b] + ld c,a ;[022e] + cp $b3 ;[022f] + jr nz,label_023b ;[0231] +label_0233: + in a,(c) ;[0233] + bit 0,a ;[0235] + jr z,label_0233 ;[0237] + jr label_0241 ;[0239] +label_023b: + in a,(c) ;[023b] + and $02 ;[023d] + jr z,label_023b ;[023f] +label_0241: + ld a,(IO_KBDCH) ;[0241] + ld c,a ;[0244] + in a,(c) ;[0245] + ld e,a ;[0247] + ld a,(IO_KBDST) ;[0248] + ld c,a ;[024b] + cp $b3 ;[024c] + jr nz,label_0258 ;[024e] +label_0250: + in a,(c) ;[0250] + bit 0,a ;[0252] + jr z,label_0250 ;[0254] + jr label_025e ;[0256] +label_0258: + in a,(c) ;[0258] + and $02 ;[025a] + jr z,label_0258 ;[025c] +label_025e: + ld a,(IO_KBDCH) ;[025e] + ld c,a ;[0261] + in a,(c) ;[0262] + ei ;[0264] + ld a,e ;[0265] Take scancode + cp $39 ;[0266] This is the spacebar scancode + jr z,label_026f ;[0268] when spacebar is pressed, break cycle + call doremap ;[026a] TODO: remap scancode + jr remap_shift_loop ;[026d] Repeat keboard reading +label_026f: + ld hl,(layout_p) ;[026f] + ld de,SLF_KEYMAP_OFFSET ;[0272] + add hl,de ;[0275] + ld (layout_p),hl ;[0276] + ld de,STR_WELCOME ;[0279] + ld c,$09 ;[027c] + call GUARD_SYSCALL ;[027e] + ld de,STR_CTRL ;[0281] + ld c,$09 ;[0284] + call GUARD_SYSCALL ;[0286] + ld a,$01 ;[0289] + ld (isnotplain),a ;[028b] + call DISPLAYKBD ;[028e] +remap_ctrl_loop: + ld de,STR_PROMPT ;[0291] + ld c,$09 ;[0294] + call GUARD_SYSCALL ;[0296] + ; The following code is same as before: catch keypress, + ; with direct I/O access, to remap scancode + di ;[0299] + ld a,(IO_KBDST) ;[029a] + ld c,a ;[029d] + cp $b3 ;[029e] + jr nz,label_02aa ;[02a0] +label_02a2: + in a,(c) ;[02a2] + bit 0,a ;[02a4] + jr z,label_02a2 ;[02a6] + jr label_02b0 ;[02a8] +label_02aa: + in a,(c) ;[02aa] + and $02 ;[02ac] + jr z,label_02aa ;[02ae] +label_02b0: + ld a,(IO_KBDCH) ;[02b0] + ld c,a ;[02b3] + in a,(c) ;[02b4] + ld e,a ;[02b6] + ld a,(IO_KBDST) ;[02b7] + ld c,a ;[02ba] + cp $b3 ;[02bb] + jr nz,label_02c7 ;[02bd] +label_02bf: + in a,(c) ;[02bf] + bit 0,a ;[02c1] + jr z,label_02bf ;[02c3] + jr label_02cd ;[02c5] +label_02c7: + in a,(c) ;[02c7] + and $02 ;[02c9] + jr z,label_02c7 ;[02cb] +label_02cd: + ld a,(IO_KBDCH) ;[02cd] + ld c,a ;[02d0] + in a,(c) ;[02d1] + ei ;[02d3] + ld a,e ;[02d4] Take scancode + cp $39 ;[02d5] This is the spacebar scancode + jr z,label_02de ;[02d7] when spacebar is pressed, break cycle + call doremap ;[02d9] TODO: remap scancode + jr remap_ctrl_loop ;[02dc] Repeat keboard reading + ; Now the changed SLF file is saved, first in a temporary + ; location with SLF*.$$$ extension. Then, old SLF*.COM file + ; is removed and SLF*.$$$ is renamed. +label_02de: + ld de,STR_REBOOT ;[02de] Print the "now reboot" message + ld c,$09 ;[02e1] C_WRITESTR + call GUARD_SYSCALL ;[02e3] + ld hl,fd+1 ;[02e6] SLF filename + ld de,tmpfd+1 ;[02e9] temporary SLF filename + ld bc,$0008 ;[02ec] filename max length + ldir ;[02ef] memcpy($0721, slf filename, 8) + ld de,tmpfd ;[02f1] fp for saving, with same name. TODO: F_MAKE will overwrite? + ld c,$16 ;[02f4] F_MAKE + call GUARD_SYSCALL ;[02f6] + ld de,STR_DIR_PLEINE ;[02f9] Prepare error message + inc a ;[02fc] F_MAKE returns $ff on error, make it $00 on error + jp z,error_epilogue ;[02fd] + ld hl,slfbuff ;[0300] Update the "dma offset" pointer to the buffer area + ld (dmaoff_p),hl ;[0303] where the file content is located + ld a,(slf_block_size) ;[0306] TODO - number of chunks to be written? + ld b,a ;[0309] +L030a: + ld de,(dmaoff_p) ;[030a] + ld c,$1a ;[030e] F_DMAOFF + call GUARD_SYSCALL ;[0310] + ld de,tmpfd ;[0313] + ld c,$15 ;[0316] F_WRITE - writes a chunk of 128 bytes + call GUARD_SYSCALL ;[0318] + ld de,STR_DISC_PLEINE ;[031b] Prepare error message + or a ;[031e] F_WRITE returns 0 on success + jp nz,error_epilogue ;[031f] + ld hl,(dmaoff_p) ;[0322] Move dma offset to the next chunk of data + ld de,$0080 ;[0325] to be written + add hl,de ;[0328] + ld (dmaoff_p),hl ;[0329] + djnz L030a ;[032c] Repeat until b > 0 + ld de,tmpfd ;[032e] + ld c,$10 ;[0331] F_CLOSE(tmpfd) + call GUARD_SYSCALL ;[0333] + ld de,fd ;[0336] + ld c,$13 ;[0339] F_DELETE(fd) + call GUARD_SYSCALL ;[033b] + ld de,srcfd ;[033e] Copy SLF filename in source descriptor + ld hl,tmpfd ;[0341] + ld bc,$0009 ;[0344] + ldir ;[0347] + ld de,dstfd ;[0349] Copy SLF filename in dest descriptor + ld hl,tmpfd ;[034c] + ld bc,$0009 ;[034f] + ldir ;[0352] + ld de,srcfd ;[0354] + ld c,$17 ;[0357] F_RENAME(srcfd,srcfd+16) + call GUARD_SYSCALL ;[0359] + jp $0000 ;[035c] Return to CP/M + +CIN_FILENAME: + ld hl,fd+1 ;[035f] filename: buffer ptr + ld b,$08 ;[0362] filename: max 8 characters (without extension) +label_0364: + ld c,$01 ;[0364] C_READ + call GUARD_SYSCALL ;[0366] + cp $0d ;[0369] + jr z,label_037f ;[036b] \n, terminate loop + cp $08 ;[036d] + jr nz,label_0375 ;[036f] backspace... + dec hl ;[0371] delete stored char + inc b ;[0372] + jr label_0364 ;[0373] continue loop +label_0375: + cp $61 ;[0375] TODO char cleanup? + jr c,label_037b ;[0377] + sub $20 ;[0379] +label_037b: + ld (hl),a ;[037b] store char + inc hl ;[037c] increment buffer ptr + djnz label_0364 ;[037d] decrement buffer ctr and loop until > 0 +label_037f: + ld hl,STR_FILENAME ;[037f] The provided filename must start with "SLF" + ld de,fd+1 ;[0382] Sanco Layout File? TODO + ld b,$03 ;[0385] +L0387: + ld a,(de) ;[0387] Compare the SLF string with the + cp (hl) ;[0388] filename string in buffer + jp nz,COUT_BADFNAME ;[0389] If mismatch, print error and terminate + inc hl ;[038c] + inc de ;[038d] + djnz L0387 ;[038e] Repeat until strlen("SLF") + ret ;[0390] + + +GUARD_SYSCALL: + push bc ;[0391] + push de ;[0392] + push hl ;[0393] + call SYSCALL ;[0394] + pop hl ;[0397] + pop de ;[0398] + pop bc ;[0399] + ret ;[039a] + +;; TODO i think display the keyboard layout +DISPLAYKBD: + ld de,STR_SYM ;[039b] + ld c,$09 ;[039e] C_WRITESTR + call GUARD_SYSCALL ;[03a0] + ld b,$10 ;[03a3] + inc hl ;[03a5] + push hl ;[03a6] + call HEXANDSPACE ;[03a7] + ld de,STR_SYM+5 ;[03aa] + ld c,$09 ;[03ad] + call GUARD_SYSCALL ;[03af] + ld b,$10 ;[03b2] + pop hl ;[03b4] + call L04D2 ;[03b5] + ld de,STR_SYM+10 ;[03b8] + ld c,$09 ;[03bb] + call GUARD_SYSCALL ;[03bd] + ld b,$0f ;[03c0] + push hl ;[03c2] + call HEXANDSPACE ;[03c3] + ld de,STR_SYM+15 ;[03c6] + ld c,$09 ;[03c9] + call GUARD_SYSCALL ;[03cb] + ld b,$0f ;[03ce] + pop hl ;[03d0] + call L04D2 ;[03d1] + ld de,STR_SYM+20 ;[03d4] + ld c,$09 ;[03d7] + call GUARD_SYSCALL ;[03d9] + ld b,$0d ;[03dc] + push hl ;[03de] + call HEXANDSPACE ;[03df] + ld de,STR_SYM+25 ;[03e2] + ld c,$09 ;[03e5] + call GUARD_SYSCALL ;[03e7] + ld b,$0d ;[03ea] + pop hl ;[03ec] + call L04D2 ;[03ed] + ld de,STR_SYM+30 ;[03f0] + ld c,$09 ;[03f3] + call GUARD_SYSCALL ;[03f5] + ld b,$0c ;[03f8] + push hl ;[03fa] + call HEXANDSPACE ;[03fb] + ld de,STR_SYM+35 ;[03fe] + ld c,$09 ;[0401] + call GUARD_SYSCALL ;[0403] + ld b,$0c ;[0406] + pop hl ;[0408] + call L04D2 ;[0409] + ld de,STR_SYM+40 ;[040c] + ld c,$09 ;[040f] + call GUARD_SYSCALL ;[0411] + ld b,$01 ;[0414] + call HEXANDSPACE ;[0416] + ld de,STR_SYM+45 ;[0419] + ld c,$09 ;[041c] + call GUARD_SYSCALL ;[041e] + ld de,STR_SYM+71 ;[0421] + ld c,$09 ;[0424] + call GUARD_SYSCALL ;[0426] + ld b,$04 ;[0429] + push hl ;[042b] + call HEXANDSPACE ;[042c] + ld de,STR_SYM+76 ;[042f] + ld c,$09 ;[0432] + call GUARD_SYSCALL ;[0434] + ld b,$04 ;[0437] + pop hl ;[0439] + call L04D2 ;[043a] + ld de,STR_SYM+81 ;[043d] + ld c,$09 ;[0440] + call GUARD_SYSCALL ;[0442] + ld b,$04 ;[0445] + push hl ;[0447] + call HEXANDSPACE ;[0448] + ld b,$04 ;[044b] + pop hl ;[044d] + ld de,STR_SYM+86 ;[044e] + ld c,$09 ;[0451] + call GUARD_SYSCALL ;[0453] + call L04D2 ;[0456] + ld de,STR_SYM+91 ;[0459] + ld c,$09 ;[045c] + call GUARD_SYSCALL ;[045e] + ld b,$04 ;[0461] + push hl ;[0463] + call HEXANDSPACE ;[0464] + ld b,$04 ;[0467] + pop hl ;[0469] + ld de,STR_SYM+96 ;[046a] + ld c,$09 ;[046d] + call GUARD_SYSCALL ;[046f] + call L04D2 ;[0472] + ld de,STR_SYM+101 ;[0475] + ld c,$09 ;[0478] + call GUARD_SYSCALL ;[047a] + ld b,$03 ;[047d] + push hl ;[047f] + call HEXANDSPACE ;[0480] + ld b,$03 ;[0483] + pop hl ;[0485] + ld de,STR_SYM+106 ;[0486] + ld c,$09 ;[0489] + call GUARD_SYSCALL ;[048b] + call L04D2 ;[048e] + ld de,STR_SYM+111 ;[0491] + ld c,$09 ;[0494] + call GUARD_SYSCALL ;[0496] + ld b,$04 ;[0499] + push hl ;[049b] + call HEXANDSPACE ;[049c] + ld b,$04 ;[049f] + pop hl ;[04a1] + ld de,STR_SYM+116 ;[04a2] + ld c,$09 ;[04a5] + call GUARD_SYSCALL ;[04a7] + call L04D2 ;[04aa] + inc hl ;[04ad] + ld de,STR_SYM+121 ;[04ae] + ld c,$09 ;[04b1] + call GUARD_SYSCALL ;[04b3] + ld b,$0f ;[04b6] + call HEXANDSPACE ;[04b8] + ret ;[04bb] + +HEXANDSPACE: + ld a,(hl) ;[04bc] + call PRHEX ;[04bd] + inc hl ;[04c0] + ld e,$20 ;[04c1] Prints space twice + ld c,$02 ;[04c3] C_WRITE + call GUARD_SYSCALL ;[04c5] + ld e,$20 ;[04c8] + ld c,$02 ;[04ca] C_WRITE + call GUARD_SYSCALL ;[04cc] + djnz HEXANDSPACE ;[04cf] while b > 0 + ret ;[04d1] + +L04D2: + ld a,(hl) ;[04d2] + cp $20 ;[04d3] + call c,PRSPECIALSWITCH ;[04d5] + cp $7f ;[04d8] + call nc,PRSPECIALSWITCH ;[04da] + or a ;[04dd] + jr z,label_04fb ;[04de] + ld e,a ;[04e0] + ld c,$02 ;[04e1] + call GUARD_SYSCALL ;[04e3] + ld e,$20 ;[04e6] + ld c,$02 ;[04e8] + call GUARD_SYSCALL ;[04ea] + ld e,$20 ;[04ed] + ld c,$02 ;[04ef] + call GUARD_SYSCALL ;[04f1] + ld e,$20 ;[04f4] + ld c,$02 ;[04f6] + call GUARD_SYSCALL ;[04f8] +label_04fb: + inc hl ;[04fb] + djnz L04D2 ;[04fc] + ret ;[04fe] + + ; TODO this routine seems to print the special character + ; strings, passed in a +PRSPECIALSWITCH: + cp $1b ;[04ff] + ld de,STR_SPECIAL+16 ;[0501] + jp z,PRSPECIALSTR ;[0504] + cp $7f ;[0507] + ld de,STR_SPECIAL+12 ;[0509] + jp z,PRSPECIALSTR ;[050c] + cp $09 ;[050f] + ld de,STR_SPECIAL+20 ;[0511] + jp z,PRSPECIALSTR ;[0514] + cp $03 ;[0517] + ld de,STR_SPECIAL+24 ;[0519] + jp z,PRSPECIALSTR ;[051c] + cp $0d ;[051f] + ld de,STR_SPECIAL+28 ;[0521] + jp z,PRSPECIALSTR ;[0524] + cp $18 ;[0527] + ld de,STR_SPECIAL+40 ;[0529] + jp z,PRSPECIALSTR ;[052c] + cp $0a ;[052f] + ld de,STR_SPECIAL+32 ;[0531] + jp z,PRSPECIALSTR ;[0534] + cp $15 ;[0537] + ld de,STR_SPECIAL+36 ;[0539] + jp z,PRSPECIALSTR ;[053c] + cp $00 ;[053f] + ld de,STR_SPECIAL ;[0541] + jp z,PRSPECIALSTR ;[0544] + cp $07 ;[0547] + ld de,STR_SPECIAL+4 ;[0549] + jp z,PRSPECIALSTR ;[054c] + cp $1a ;[054f] + ld de,STR_SPECIAL+8 ;[0551] + jp z,PRSPECIALSTR ;[0554] + cp $0b ;[0557] + jr nz,label_055d ;[0559] + ld a,$f1 ;[055b] +label_055d: + cp $0c ;[055d] + jr nz,label_0563 ;[055f] + ld a,$f3 ;[0561] +label_0563: + cp $08 ;[0563] + jr nz,label_0569 ;[0565] + ld a,$f4 ;[0567] +label_0569: + cp $1d ;[0569] + jr nz,label_056f ;[056b] + ld a,$20 ;[056d] +label_056f: + cp $1e ;[056f] + ret nz ;[0571] + ld a,$20 ;[0572] + ret ;[0574] + +PRSPECIALSTR: + ld c,$09 ;[0575] C_WRITESTR + call GUARD_SYSCALL ;[0577] + ld e,$20 ;[057a] + ld c,$02 ;[057c] C_WRITE + call GUARD_SYSCALL ;[057e] + xor a ;[0581] + ret ;[0582] + +doremap: + ld hl,(layout_p) ;[0583] + ld c,a ;[0586] The scancode is in bc, with most significant + ld b,$00 ;[0587] bits zeroed + add hl,bc ;[0589] Compute the key displacement in keymap table + ld a,(hl) ;[058a] load the char currently mapped to the scancode + ld c,a ;[058b] + ld a,(isnotplain) ;[058c] TODO this is 0 or 1? + cp $01 ;[058f] + ld a,c ;[0591] + jp z,L0628 ;[0592] jump if CTRL or SHIFT? + cp $80 ;[0595] + jp c,L0628 ;[0597] jump if a < $80 + cp $85 ;[059a] + jp nc,L0628 ;[059c] jump if a >= $85 + ld hl,slfbuff ;[059f] + ld de,$1f80 ;[05a2] + add hl,de ;[05a5] + sub $7f ;[05a6] + ld de,$000e ;[05a8] +label_05ab: + dec a ;[05ab] + jr z,label_05b1 ;[05ac] + add hl,de ;[05ae] + jr label_05ab ;[05af] + +label_05b1: + ld (charmap_p),hl ;[05b1] +label_05b4: + ld a,(hl) ;[05b4] TODO loop that prints *hl++ + cp $0d ;[05b5] + jr nz,label_05bb ;[05b7] + ld a,$f2 ;[05b9] +label_05bb: + cp $7f ;[05bb] + jr z,label_05c8 ;[05bd] + ld e,a ;[05bf] + ld c,$02 ;[05c0] C_WRITE + call GUARD_SYSCALL ;[05c2] + inc hl ;[05c5] + jr label_05b4 ;[05c6] + +label_05c8: + ld hl,(charmap_p) ;[05c8] + ld b,$04 ;[05cb] +L05cd: + ld a,(hl) ;[05cd] + ld e,a ;[05ce] + ld c,$02 ;[05cf] C_WRITE + call GUARD_SYSCALL ;[05d1] + inc hl ;[05d4] + djnz L05cd ;[05d5] + ld b,$08 ;[05d7] + xor a ;[05d9] + ld (newchar),a ;[05da] +label_05dd: + ld c,$06 ;[05dd] TODO C_RAWIO + ld e,$ff ;[05df] TODO Return a character without echoing if one is waiting; zero if none is available. In MP/M 1, this works like E=0FDh below and waits for a character. + call GUARD_SYSCALL ;[05e1] + or a ;[05e4] zero = no char available + jr z,label_05dd ;[05e5] + cp $5c ;[05e7] TODO "\" + jr z,label_0622 ;[05e9] + cp $0d ;[05eb] TODO \n + jr nz,label_05fd ;[05ed] + ld c,a ;[05ef] + ld a,(newchar) ;[05f0] + or a ;[05f3] + jp z,L0636 ;[05f4] + ld (hl),c ;[05f7] + inc hl ;[05f8] + ld e,$f2 ;[05f9] + jr label_061b ;[05fb] + +label_05fd: + cp $08 ;[05fd] + jr nz,label_060b ;[05ff] + dec hl ;[0601] + inc b ;[0602] + ld e,a ;[0603] + ld c,$02 ;[0604] + call GUARD_SYSCALL ;[0606] + jr label_05dd ;[0609] + +label_060b: + cp $61 ;[060b] + jr c,label_0615 ;[060d] + cp $7b ;[060f] + jr nc,label_0615 ;[0611] + sub $20 ;[0613] +label_0615: + ld (newchar),a ;[0615] + ld (hl),a ;[0618] + inc hl ;[0619] + ld e,a ;[061a] +label_061b: + ld c,$02 ;[061b] C_WRITE + call GUARD_SYSCALL ;[061d] + djnz label_05dd ;[0620] +label_0622: + ld a,$7f ;[0622] + ld (hl),a ;[0624] + jp L0636 ;[0625] + +L0628: + call PRHEX ;[0628] + ld de,STR_NOUVELLE ;[062b] + ld c,$09 ;[062e] C_WRITESTR + call GUARD_SYSCALL ;[0630] + call L0644 ;[0633] +L0636: + ld de,STR_SPACES ;[0636] + ld c,$09 ;[0639] C_WRITESTR + call GUARD_SYSCALL ;[063b] + ld hl,(layout_p) ;[063e] + jp DISPLAYKBD ;[0641] + +L0644: + ld de,newcharbuf ;[0644] + ld b,$02 ;[0647] +label_0649: + ld c,$01 ;[0649] C_READ + call GUARD_SYSCALL ;[064b] + cp $0d ;[064e] + ret z ;[0650] \n, terminate loop + cp $08 ;[0651] backspace... + jr nz,label_0659 ;[0653] + dec de ;[0655] delete stored char + inc b ;[0656] + jr label_0649 ;[0657] continue loop +label_0659: + cp $30 ;[0659] + jr c,label_0694 ;[065b] jump if a < "0" + cp $67 ;[065d] + jr nc,label_0694 ;[065f] jump if a >= 'g' + cp $61 ;[0661] + jr nc,label_066b ;[0663] jump if a >= 'a' (and < 'g') + cp $47 ;[0665] + jr nc,label_0694 ;[0667] jump if a >= 'G' + jr label_066d ;[0669] +label_066b: + ; I think there is an error here: the add is applied to + ; lowercase chars only (i.e. $61 + $20 = $81 = ???). + ; But it should be applied to uppercase + ; (i.e. 'A' + $20 = $41 + $20 = $61 = 'a') + add $20 ;[066b] TODO +label_066d: + ld (de),a ;[066d] TODO what was in DE? + inc de ;[066e] + djnz label_0649 ;[066f] loop until b>0 + ld de,newcharbuf ;[0671] + ld a,(de) ;[0674] + cp $41 ;[0675] + jr c,label_067b ;[0677] jump if a < 'A' + sub $07 ;[0679] +label_067b: + sub $30 ;[067b] + and $0f ;[067d] + rra ;[067f] put this digit in upper nibble + rra ;[0680] through rotation + rra ;[0681] TODO: 5 times because is through carry? + rra ;[0682] + rra ;[0683] + ld c,a ;[0684] + inc de ;[0685] + ld a,(de) ;[0686] + cp $41 ;[0687] Do the same thing as before... + jr c,label_068d ;[0689] + sub $07 ;[068b] +label_068d: + sub $30 ;[068d] + and $0f ;[068f] + add c ;[0691] Recompose the whole number + ld (hl),a ;[0692] + ret ;[0693] + +label_0694: + ld c,$02 ;[0694] C_WRITE + ld e,$08 ;[0696] Backspace? + call GUARD_SYSCALL ;[0698] + jp label_0649 ;[069b] + + ; PRHEX(a) +PRHEX: + push af ;[069e] + rra ;[069f] + rra ;[06a0] + rra ;[06a1] + rra ;[06a2] a >>= 4 + call PRHEXNIBBLE ;[06a3] + pop af ;[06a6] + call PRHEXNIBBLE ;[06a7] + ret ;[06aa] + + ; PRHEXNIBBLE(a) +PRHEXNIBBLE: + and $0f ;[06ab] + cp $0a ;[06ad] + jr c,label_06b3 ;[06af] + add $07 ;[06b1] +label_06b3: + add $30 ;[06b3] + ld e,a ;[06b5] + ld c,$02 ;[06b6] C_WRITE + call GUARD_SYSCALL ;[06b8] + ret ;[06bb] + +err_nofile: + ld de,STR_MODULE ;[06bc] + ld c,$09 ;[06bf] C_WRITESTR + call GUARD_SYSCALL ;[06c1] + ld hl,fd+1 ;[06c4] + ld b,$08 ;[06c7] +L06c9: + ld a,(hl) ;[06c9] + cp $20 ;[06ca] + jr z,label_06d7 ;[06cc] + ld e,a ;[06ce] + ld c,$02 ;[06cf] C_WRITE + call GUARD_SYSCALL ;[06d1] + inc hl ;[06d4] + djnz L06c9 ;[06d5] +label_06d7: + ld de,STR_INEXISTANT ;[06d7] + ld c,$09 ;[06da] C_WRITESTR + call GUARD_SYSCALL ;[06dc] + jp $0000 ;[06df] Reset + +error_epilogue: + ld c,$09 ;[06e2] C_WRITESTR + call GUARD_SYSCALL ;[06e4] + ld de,tmpfd ;[06e7] + ld c,$13 ;[06ea] F_DELETE(tmpfd) + call GUARD_SYSCALL ;[06ec] + jp $0000 ;[06ef] Reset + +COUT_BADFNAME: + ld de,STR_BADFNAME ;[06f2] + ld c,$09 ;[06f5] C_WRITESTR + call GUARD_SYSCALL ;[06f7] + jp $0000 ;[06fa] Reset + + ; File descriptors area: they are used with F_OPEN, F_READ, + ; F_WRITE, ... + + ; The file descriptor used to read the SLF file + ; (*.COM) +fd: ;[06fd] + DB 0x01 + ;[06fe] + DB " COM" + + nop ;[0709] 00 + nop ;[070a] 00 + nop ;[070b] 00 + nop ;[070c] 00 + nop ;[070d] 00 + nop ;[070e] 00 + nop ;[070f] 00 + nop ;[0710] 00 + nop ;[0711] 00 + nop ;[0712] 00 + nop ;[0713] 00 + nop ;[0714] 00 + nop ;[0715] 00 + nop ;[0716] 00 + nop ;[0717] 00 + nop ;[0718] 00 + nop ;[0719] 00 + nop ;[071a] 00 + nop ;[071b] 00 + nop ;[071c] 00 + nop ;[071d] 00 + nop ;[071e] 00 + nop ;[071f] 00 + + ; The file descriptor used to store temporary SLF file + ; (*.$$$) +tmpfd: ;[0720] + DB 0x01 + ;[0721] + DB " $$$" + nop ;[072c] 00 + nop ;[072d] 00 + nop ;[072e] 00 + nop ;[072f] 00 + nop ;[0730] 00 + nop ;[0731] 00 + nop ;[0732] 00 + nop ;[0733] 00 + nop ;[0734] 00 + nop ;[0735] 00 + nop ;[0736] 00 + nop ;[0737] 00 + nop ;[0738] 00 + nop ;[0739] 00 + nop ;[073a] 00 + nop ;[073b] 00 + nop ;[073c] 00 + nop ;[073d] 00 + nop ;[073e] 00 + nop ;[073f] 00 + nop ;[0740] 00 + nop ;[0741] 00 + nop ;[0742] 00 + + ; Special file descriptors used with F_RENAME to move + ; temporary *.$$$ file as *.COM +srcfd: ;[0743] + DB 0 + DB " $$$" + DB 0,0,0,0 +dstfd: ;[0753] + DB 0 + DB " COM" + DB 0,0,0,0 + + ; [0763] +STR_WELCOME: + DB "\x1A\x0D\x0A\x09\x09\x09\x1B\x37\x1B\x41" + DB "** FUNK00 **" + DB "\x1B\x42\x1B\x58" + DB "$" + + ; [077e] +STR_GETFILE: + DB "\x0D\x0A\x0A\x0A\x0A" + DB "Entrez le nom du module a modifier :" + DB "$" + + ;[07b3] +STR_NORMAL: + DB "\x0D\x0A\x0A" + DB "Valeur des touches en mode " + DB "\x1B\x48" + DB "NORMAL" + DB "\x1B\x49" + DB "$" + +STR_SHIFT: ;[07dc] + DB "\x0D\x0A\x0A" + DB "Valeur des touches en mode " + DB "\x1B\x48" + DB "SHIFT" + DB "\x1B\x49$" + +STR_CTRL: ;[0804] + DB "\x0D\x0A\x0A" + DB "Valeur des touches en mode " + DB "\x1B\x48" + DB "CONTROL" + DB "\x1B\x49$" + +STR_REBOOT: ;[082e] + DB "\x0D\x0A\x1B\x48" + DB "Rebooter pour installer le nouveau clavier" + DB "\x1B\x49$" + +STR_MODULE: ;[0863] + DB "\x0d\x0a\x0a\x07Module $" + +STR_INEXISTANT: ;[086b] + DB ".COM inexistant sur cette disquette$" + +STR_DIR_PLEINE: ;[088f] + DB "\x0d\x0a\x0a\x07Creation impossible\x2e\x2e\x2eDirectorie pleine$" + +STR_DISC_PLEINE: ;[08bb] + DB "\x0d\x0a\x0a\x07Erreur ecriture\x2e\x2e\x2eDisquette pleine$" + +STR_BADFNAME: ;[08e2] + DB "\x0D\x0A\x0A\x07" + DB "Le fichier n est pas un $" + +STR_PROMPT: ;[0909] + DB "\x1B\x3D\x37" + DB " Appuyez sur le caractere a modifier : " + DB "$" + +STR_SPACES: ;[0934] + DB "\x1B\x3D\x37" + DB " $" + +STR_SYM: ;[097d] + DB "\x1b=(!$" + DB "\x1b=)!$" + DB "\x1b=+#$" + DB "\x1b=,#$" + DB "\x1b=\x2e,$" + DB "\x1b=/,$" + DB "\x1b=10$" + DB "\x1b=20$" + DB "\x1b=46$" + DB "\x1b=53SPACE (Ecran suivant)$" + DB "\x1b=%a$" + DB "\x1b=&a$" + DB "\x1b=(a$" + DB "\x1b=)a$" + DB "\x1b=+a$" + DB "\x1b=,a$" + DB "\x1b=\x2ea$" + DB "\x1b=/a$" + DB "\x1b=1a$" + DB "\x1b=2a$" + DB "\x1b=% $" + +STR_NOUVELLE: ;[09fb] + DB " Nouvelle valeur (HEXA) :$" + +STR_INCOMPATIBLE: ;[0a15] + DB "\x0D\x0A" + DB "Machine incompatible avec ce programme" + DB "$" + + ;[0a3e] +STR_FILENAME: + DB "SLF" + +STR_SPECIAL: ;[0a41] + DB "00 $" + DB "BEL$" + DB "CLS$" + DB "DEL$" + DB "ESC$" + DB "TAB$" + DB "BRK$" + DB "RET$" + DB "LF $" + DB "CE $" + DB "CAN$" + + nop ;[0a6d] 00 + nop ;[0a6e] 00 + nop ;[0a6f] 00 + nop ;[0a70] 00 + nop ;[0a71] 00 + nop ;[0a72] 00 + nop ;[0a73] 00 + nop ;[0a74] 00 + ; Points to the current keyboard layout (NORMAL, SHIFT, + ; CONTROL). The pointed data is inside slfbuff. +layout_p: + DW 0x0000 ;[0a75] + ; Points to the slfbuff area used to load the chunks + ; of the SLF file (i.e. the buffer where the file is placed) +dmaoff_p: + DW 0x0000 ;[0a77] + ; Buffer for the new character to be mapped to the selected + ; scancode +newcharbuf: + DW 0x0000 ;[0a79] + ; Pointer to characters inside slfbuff, used to print each + ; character on screen to dispay current keymap +charmap_p: + DW 0x0000 ;[0a7b] + +IO_KBDST: ;[0a7d] + DB 0 + +IO_KBDCH: ;[0a7e] + DB 0 + ; TODO: may be the new character value to be assigned to a + ; certain scancode. But I have to verify this. +newchar: + DB 0 ;[0a7f] + ; TODO. it is 0 when handling NORMAL, 1 when SHIFT or CONTROL +isnotplain: + DB 0 ;[0a80] +slf_block_size: + DB 0 ;[0a81] + + or a ;[0a82] b7 + nop ;[0a83] 00 + nop ;[0a84] 00 + nop ;[0a85] 00 + nop ;[0a86] 00 + nop ;[0a87] 00 + nop ;[0a88] 00 + nop ;[0a89] 00 + nop ;[0a8a] 00 + nop ;[0a8b] 00 + nop ;[0a8c] 00 + nop ;[0a8d] 00 + nop ;[0a8e] 00 + nop ;[0a8f] 00 + nop ;[0a90] 00 + nop ;[0a91] 00 + nop ;[0a92] 00 + nop ;[0a93] 00 + nop ;[0a94] 00 + nop ;[0a95] 00 + nop ;[0a96] 00 + nop ;[0a97] 00 + nop ;[0a98] 00 + nop ;[0a99] 00 + nop ;[0a9a] 00 + nop ;[0a9b] 00 + nop ;[0a9c] 00 + nop ;[0a9d] 00 + nop ;[0a9e] 00 + nop ;[0a9f] 00 + nop ;[0aa0] 00 + nop ;[0aa1] 00 + nop ;[0aa2] 00 + nop ;[0aa3] 00 + nop ;[0aa4] 00 + nop ;[0aa5] 00 + nop ;[0aa6] 00 + nop ;[0aa7] 00 + nop ;[0aa8] 00 + nop ;[0aa9] 00 + nop ;[0aaa] 00 + nop ;[0aab] 00 + nop ;[0aac] 00 + nop ;[0aad] 00 + nop ;[0aae] 00 + nop ;[0aaf] 00 + nop ;[0ab0] 00 + nop ;[0ab1] 00 + nop ;[0ab2] 00 + nop ;[0ab3] 00 + + ; the buffer area where the SLF file is loaded. + ; Note: the stack starts from here and grows to lower + ; addresses +slfbuff: ;[0ab4] + DB "programme$" + DB "SLF00 $" + DB "BEL$" + DB "CLS$" + DB "DEL$" + DB "ESC$" + DB "TAB$" + DB "BRK$" + DB "RET$" + DB "LF $" + DB "CE $" + DB "CAN$" + + nop ;[0aed] 00 + nop ;[0aee] 00 + nop ;[0aef] 00 + nop ;[0af0] 00 + nop ;[0af1] 00 + nop ;[0af2] 00 + nop ;[0af3] 00 + nop ;[0af4] 00 + nop ;[0af5] 00 + nop ;[0af6] 00 + nop ;[0af7] 00 + nop ;[0af8] 00 + nop ;[0af9] 00 + nop ;[0afa] 00 + nop ;[0afb] 00 + nop ;[0afc] 00 + nop ;[0afd] 00 + nop ;[0afe] 00 + nop ;[0aff] 00 diff --git a/applications/Makefile b/applications/Makefile new file mode 100644 index 0000000..e473cd3 --- /dev/null +++ b/applications/Makefile @@ -0,0 +1,44 @@ +.POSIX: + +ECHO := @echo +QUIET := @ + +ASM := FUNK00.COM.asm +ASM += PAR8003.COM.asm +ASM += PAR8003.relocated.asm +ASM += SLF80037.COM.asm +ASM += SLF80037.relocated.asm +ASM += COPY8003.COM.asm +ASM += SG8003.COM.asm +ASM += TRX62.COM.asm + +CHK_FILENAME := md5sum.txt + +BIN := $(patsubst %.asm, build/%, $(ASM)) + +CHK := $(patsubst %.asm, %.chk, $(ASM)) + +all: tests + +.PHONY: tests +tests: checksum + +.PHONY: checksum +checksum: $(CHK) + +.PHONY: assemble +assemble: $(BIN) + +build/%: %.asm + $(QUIET) mkdir -p `dirname $@` + $(ECHO) ' ASM $<' + $(QUIET) z80asm -b -o$@ $< + +%.chk: build/% + $(ECHO) ' SUM $<' + $(QUIET) grep "`md5sum "$<"`" $(CHK_FILENAME) > /dev/null + +.PHONY: clean +clean: + $(ECHO) ' RM' + $(QUIET) rm -rf build diff --git a/applications/PAR8003.COM.asm b/applications/PAR8003.COM.asm new file mode 100644 index 0000000..d440c0d --- /dev/null +++ b/applications/PAR8003.COM.asm @@ -0,0 +1,347 @@ +; PAR8003.COM, used to reconfigure secondary drive on a Sanco computer. +; Or, at least, this is what I've understood. + + org 0x0100 + + nop ;[0100] they like to lose time? + nop ;[0101] + nop ;[0102] +main: + ld sp,$07ca ;[0103] configure stack pointer + call initialize ;[0106] + call putstr ;[0109] print the following string on screen until NUL character, + ; then resume execution from the first instruction after NUL. + + ;[010c] + DB "\r\n" + DB "\r\n" + DB "\xed\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xeb" + DB "\r\n" + DB "\xef PARADISC vers 1.2 SANCO-IBEX 8003 \xef" + DB "\r\n" + DB "\xef Programme de modification de parametres disque \xef" + DB "\r\n" + DB "\xee\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xe5\xec" + DB "\r\n" + DB "\r\n" + DB 0 + + call putstr ;[01eb] print the following string on screen, until NUL character + + ;[01ee] + DB "\r\n" + DB "\r\n" + DB " ^C retour sous systeme " + DB "\r\n" + DB " param\x2eSANCO-IBEX " + DB "\r\n" + DB " -1- \" \" 8103 7103 " + DB "\r\n" + DB " -2- \" 8001 8102 7102/2 " + DB "\r\n" + DB " -3- \" \" \" 7102 " + DB "\r\n" + DB "\r\n" + DB " Code ?:" + DB 0 + +get_key: + call cpm_read ;[02e7] wait for keypress + or a ;[02ea] + jp z,get_key ;[02eb] + cp $03 ;[02ee] ^C to exit + jp z,quit ;[02f0] + ld (bSelection),a ;[02f3] save the inserted character + ld hl,table1 ;[02f6] + cp '1' ;[02f9] + jp z,apply_settings ;[02fb] + ld hl,table2 ;[02fe] + cp '2' ;[0301] + jp z,apply_settings ;[0303] + ld hl,table3 ;[0306] + cp '3' ;[0309] + jp z,apply_settings ;[030b] + ld a,'\a' ;[030e] + call cpm_rawio ;[0310] trigger system bell + jp main ;[0313] restart from the beginning + +cpm_read: + push hl ;[0316] + push de ;[0317] + push bc ;[0318] + ld c,$01 ;[0319] + call $0005 ;[031b] + pop bc ;[031e] + pop de ;[031f] + pop hl ;[0320] + ret ;[0321] + +initialize: + ld hl,($0001) ;[0322] take the system reset entry from the CP/M jump table (WBOOT) + ld de,$0018 ;[0325] add +0x18 to get the address of SELDSK + add hl,de ;[0328] + ld (trampoline+1),hl ;[0329] make the trampoline point there + ret ;[032c] + + ; TODO trampoline +trampoline: + jp $0000 ;[032d] + ret ;[0330] this return is quite useless... + +apply_settings: + ld a,$43 ;[0331] + dec a ;[0333] + push hl ;[0334] save hl (the value from the previous switch) + sub $41 ;[0335] pretty complicate way to do ld a,$1 + ld c,a ;[0337] copy $1 in c + ld b,$01 ;[0338] + and $03 ;[033a] limit a between 0 and 3 + jr z,label_0343 ;[033c] calculate the mask 1 << a +label_033e: + rlc b ;[033e] + dec a ;[0340] + jr nz,label_033e ;[0341] +label_0343: + ld a,b ;[0343] + cpl ;[0344] negate the mask a = ~a, should be 0xFD + ld hl,$ffc7 ;[0345] this is populated by FDC routines, keeps the mask of already initialized disks + and (hl) ;[0348] + ld (hl),a ;[0349] the result is still 1 + call trampoline ;[034a] manually call SELDSK from CP/M bios. + ; Invoked with c=1 (disk 1). Undocumented: DE points to the + ; DPB of selected disk. + pop hl ;[034d] restore hl, size of the new DPB table + ld c,(hl) ;[034e] + inc hl ;[034f] + ld b,(hl) ;[0350] + inc hl ;[0351] now hl points to DPB table, bc holds its size + ldir ;[0352] overwrite old DPB with new one + ld a,(bSelection) ;[0354] + cp '1' ;[0357] + jr z,label_0381 ;[0359] treat 1 (and 4 !?!) as special case + cp '4' ;[035b] (TODO) WHAT THE HELL, THIS IS NOT EVEN POSSIBLE! + jr z,label_0381 ;[035d] + di ;[035f] + in a,($81) ;[0360] disable bank switch to access AUX memory + res 0,a ;[0362] + out ($81),a ;[0364] + ld de,$bc00 ;[0366] copy the new fdc_routines in AUX RAM. + ld hl,fdc_routines ;[0369] these fdc_routines contain a reimplementation of the rwfs routines + ld bc,$02da ;[036c] + ldir ;[036f] + ld hl,$bc00 ;[0371] + ld ($b819),hl ;[0374] update the pointer to rwfs routine in ROM jump table copy + in a,($81) ;[0377] enable bank switch + set 0,a ;[0379] + out ($81),a ;[037b] + ei ;[037d] + jp quit ;[037e] +label_0381: + di ;[0381] + in a,($81) ;[0382] + res 0,a ;[0384] + out ($81),a ;[0386] + ld hl,$c018 ;[0388] just keep ROM's rwfs as the right fdc routine + ld ($b819),hl ;[038b] + in a,($81) ;[038e] + set 0,a ;[0390] + out ($81),a ;[0392] + ei ;[0394] + jp quit ;[0395] + +fdc_routines: ;[0398] + ;; RELOCATED CODE!!! + ; This code is relocated from $bc00 to $beda. + ; See PAR8003.relocated.asm + DB $C5,$D5,$E5,$21,$D7,$BE,$36,$00,$CB,$7F,$28,$02,$36,$01,$CB,$BF + DB $32,$B8,$FF,$E1,$E5,$3E,$0A,$32,$BF,$FF,$ED,$43,$B9,$FF,$ED,$53 + DB $BB,$FF,$22,$BD,$FF,$CD,$9E,$BE,$3A,$BA,$FF,$E6,$F0,$CA,$58,$BC + DB $FE,$40,$CA,$4E,$BC,$FE,$80,$CA,$49,$BC,$FE,$20,$CA,$53,$BC,$FE + DB $F0,$CA,$5D,$BC,$3E,$FF,$C3,$62,$BC,$CD,$66,$BC,$18,$14,$CD,$BC + DB $BC,$18,$0F,$CD,$1B,$BE,$18,$0A,$CD,$03,$BE,$18,$05,$CD,$55,$BD + DB $18,$00,$E1,$D1,$C1,$C9,$CD,$1B,$BE,$CD,$29,$BD,$D5,$CD,$97,$BE + DB $0E,$C5,$3A,$B8,$FF,$B7,$20,$02,$CB,$B1,$CD,$90,$BE,$F3,$CD,$C0 + DB $BD,$D1,$0E,$C1,$43,$2A,$BD,$FF,$DB,$82,$CB,$57,$28,$FA,$DB,$C0 + DB $CB,$6F,$28,$07,$ED,$A3,$20,$F0,$15,$20,$ED,$D3,$DC,$FB,$CD,$6F + DB $BE,$3A,$C0,$FF,$E6,$C0,$FE,$40,$20,$10,$CD,$12,$BD,$3A,$BF,$FF + DB $3D,$32,$BF,$FF,$C2,$69,$BC,$3E,$FF,$C9,$AF,$C9,$CD,$1B,$BE,$CD + DB $29,$BD,$D5,$CD,$97,$BE,$0E,$C6,$3A,$B8,$FF,$B7,$20,$02,$CB,$B1 + DB $CD,$90,$BE,$F3,$CD,$C0,$BD,$D1,$0E,$C1,$43,$2A,$BD,$FF,$DB,$82 + DB $CB,$57,$28,$FA,$DB,$C0,$CB,$6F,$28,$07,$ED,$A2,$20,$F0,$15,$20 + DB $ED,$D3,$DC,$FB,$CD,$6F,$BE,$3A,$C0,$FF,$E6,$C0,$FE,$40,$20,$10 + DB $CD,$12,$BD,$3A,$BF,$FF,$3D,$32,$BF,$FF,$C2,$BF,$BC,$3E,$FF,$C9 + DB $AF,$C9,$3A,$C2,$FF,$CB,$67,$28,$04,$CD,$03,$BE,$C9,$3A,$C1,$FF + DB $CB,$47,$28,$04,$CD,$03,$BE,$C9,$C9,$1E,$00,$3A,$B8,$FF,$FE,$03 + DB $20,$14,$16,$04,$3A,$BB,$FF,$CB,$7F,$28,$19,$3A,$BA,$FF,$E6,$0F + DB $07,$07,$82,$57,$18,$0E,$B7,$20,$02,$1E,$80,$3A,$BA,$FF,$E6,$0F + DB $16,$01,$82,$57,$C9,$CD,$1B,$BE,$FE,$FF,$C8,$06,$14,$3A,$B8,$FF + DB $FE,$03,$28,$02,$06,$40,$C5,$CD,$97,$BE,$0E,$4D,$CD,$90,$BE,$ED + DB $4B,$B9,$FF,$CD,$90,$BE,$3A,$B8,$FF,$4F,$CD,$90,$BE,$0E,$05,$3A + DB $B8,$FF,$FE,$03,$28,$02,$0E,$10,$CD,$90,$BE,$0E,$28,$CD,$90,$BE + DB $F3,$0E,$E5,$CD,$90,$BE,$C1,$0E,$C1,$2A,$BD,$FF,$DB,$82,$CB,$57 + DB $28,$FA,$DB,$C0,$CB,$6F,$28,$04,$ED,$A3,$20,$F0,$D3,$DC,$FB,$CD + DB $6F,$BE,$3A,$C0,$FF,$E6,$C0,$FE,$40,$20,$03,$3E,$FF,$C9,$AF,$C9 + DB $ED,$4B,$B9,$FF,$CD,$90,$BE,$ED,$5B,$BB,$FF,$4A,$CD,$90,$BE,$ED + DB $4B,$B9,$FF,$79,$E6,$04,$0F,$0F,$4F,$CD,$90,$BE,$CB,$BB,$4B,$0C + DB $CD,$90,$BE,$3A,$B8,$FF,$4F,$CD,$90,$BE,$0E,$05,$3A,$B8,$FF,$FE + DB $03,$28,$02,$0E,$10,$CD,$90,$BE,$0E,$28,$CD,$90,$BE,$0E,$FF,$CD + DB $90,$BE,$C9,$CD,$97,$BE,$0E,$07,$CD,$90,$BE,$ED,$4B,$B9,$FF,$CB + DB $91,$CD,$90,$BE,$CD,$4D,$BE,$28,$EA,$AF,$C9,$ED,$5B,$BB,$FF,$7A + DB $B7,$CA,$03,$BE,$CD,$97,$BE,$0E,$0F,$CD,$90,$BE,$ED,$4B,$B9,$FF + DB $CD,$90,$BE,$21,$D7,$BE,$CB,$46,$28,$02,$CB,$22,$4A,$CD,$90,$BE + DB $CD,$4D,$BE,$20,$06,$CD,$03,$BE,$C3,$1B,$BE,$AF,$C9,$DB,$82,$CB + DB $57,$CA,$4D,$BE,$CD,$97,$BE,$CD,$7E,$BE,$3E,$08,$D3,$C1,$CD,$87 + DB $BE,$DB,$C1,$47,$CD,$87,$BE,$DB,$C1,$78,$E6,$C0,$FE,$40,$C9,$21 + DB $C0,$FF,$06,$07,$0E,$C1,$CD,$87,$BE,$ED,$A2,$20,$F9,$C9,$DB,$C0 + DB $07,$30,$FB,$07,$38,$F8,$C9,$DB,$C0,$07,$30,$FB,$07,$30,$F8,$C9 + DB $CD,$7E,$BE,$79,$D3,$C1,$C9,$DB,$C0,$CB,$67,$20,$FA,$C9,$06,$01 + DB $79,$E6,$03,$B7,$28,$05,$CB,$00,$3D,$20,$FB,$3A,$C7,$FF,$4F,$A0 + DB $C0,$79,$B0,$32,$C7,$FF,$CD,$03,$BE,$C9,$C5,$E5,$21,$D8,$BE,$CD + DB $97,$BE,$0E,$03,$CD,$90,$BE,$4E,$23,$CD,$90,$BE,$4E,$CD,$90,$BE + DB $AF,$32,$C7,$FF,$E1,$C1,$C9,$00,$6F,$1B +;; END OF RELOCATION + +putstr: + pop de ;[0672] take the return address from the stack +putstr_loop: + ld a,(de) ;[0673] take its content (it should be a character) + or a ;[0674] if NUL... + jp z,putstr_epilogue ;[0675] ...terminate the execution + call cpm_rawio ;[0678] put a on screen + inc de ;[067b] move data pointer and repeat + jp putstr_loop ;[067c] +putstr_epilogue: + inc de ;[067f] skip NUL character + ex de,hl ;[0680] + jp (hl) ;[0681] then manually return to the first instruction after NUL + +; TODO: rawio outputs character if e < 0xFF, else will read +; What's the difference with putchar??? +cpm_rawio: + push hl ;[0682] + push de ;[0683] + push bc ;[0684] + ld c,$06 ;[0685] TODO C_RAWIO + ld e,a ;[0687] + call $0005 ;[0688] + pop bc ;[068b] + pop de ;[068c] + pop hl ;[068d] + ret ;[068e] + +quit: + jp $0000 ;[068f] TODO invoke CP/M warm reset + +table1: + DB $1d,$00 ;[0692] + ; DPB, choice "1" + ; This is not even used, since if "1" is selected, this is discarded and + ; the ROM rwfs is used. + DW 80 ; Number of 128-byte records per track + DB 5 ; Block shift. 3 => 1k, 4 => 2k, 5 => 4k + DB 0x1f ; Block mask. 7 => 1k, 0Fh => 2k, 1Fh => 4k... + DB 03 ; Extent mask + DW 195 ; (no. of blocks on the disk)-1 + DW 0x7f ; (no. of directory entries)-1 + DB 0x80 ; Directory allocation bitmap, first byte + DB 0 ; Directory allocation bitmap, second byte + DW 0x0020 ; Checksum vector size, 0 for a fixed disk + ; No. directory entries/4, rounded up. + DB 1 ; Offset, number of reserved tracks + + ; Just garbage, I think... + DB $00,$20,$50,$07,$03,$01,$03,$05,$00,$00,$00,$02,$04,$01,$03 + +table2: + DB $1d,$00 ;[06b1] + ; DPB, choice "2" + DW 80 ; Number of 128-byte records per track + DB 4 ; Block shift. 3 => 1k, 4 => 2k, 5 => 4k + DB 0x0f ; Block mask. 7 => 1k, 0Fh => 2k, 1Fh => 4k... + DB 00 ; Extent mask + DW 194 ; (no. of blocks on the disk)-1 + DW 0x3f ; (no. of directory entries)-1 + DB 0x80 ; Directory allocation bitmap, first byte + DB 0 ; Directory allocation bitmap, second byte + DW 0x0010 ; Checksum vector size, 0 for a fixed disk + ; No. directory entries/4, rounded up. + DB 1 ; Offset, number of reserved tracks + + ; Just garbage, I think... + DB $00,$10,$50,$07,$83,$01,$03,$05,$00,$00,$00,$02,$04,$01,$03 + +table3: + DB $28,$00 ;[06d0] + ; DPB for disk, choice "3" + DW 64 ; Number of 128-byte records per track + DB 3 ; Block shift. 3 => 1k, 4 => 2k, 5 => 4k + DB 0x07 ; Block mask. 7 => 1k, 0Fh => 2k, 1Fh => 4k... + DB 00 ; Extent mask + DW 255 ; (no. of blocks on the disk)-1 + DW 0x3f ; (no. of directory entries)-1 + DB 192 ; Directory allocation bitmap, first byte + DB 0 ; Directory allocation bitmap, second byte + DW 0x0010 ; Checksum vector size, 0 for a fixed disk + ; No. directory entries/4, rounded up. + DB 1 ; Offset, number of reserved tracks + + ; Just garbage, I think... + DB $00,$08,$40,$01,$81,$01,$00,$10,$00,$00,$00,$02,$04,$06,$08,$0a,$0c,$0e,$01,$03,$05,$07,$09,$0b,$0d,$0f + + DB $32,$00 ;[06fa] + ; DPB for disk 0, fake choice "4" + DW 128 ; Number of 128-byte records per track + DB 5 ; Block shift. 3 => 1k, 4 => 2k, 5 => 4k + DB 0x1f ; Block mask. 7 => 1k, 0Fh => 2k, 1Fh => 4k... + DB 00 ; Extent mask + DW 299 ; (no. of blocks on the disk)-1 + DW 0x7f ; (no. of directory entries)-1 + DB 128 ; Directory allocation bitmap, first byte + DB 0 ; Directory allocation bitmap, second byte + DW 0x0020 ; Checksum vector size, 0 for a fixed disk + ; No. directory entries/4, rounded up. + DB 2 ; Offset, number of reserved tracks + + ; Just garbage, I think... + DB $00,$20,$80,$07,$03,$82,$03,$08,$00,$00,$00,$03,$06,$01,$04,$07,$02,$05,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 + + DB $32,$00 ;[072e] + ; DPB for disk 0, fake choice "5" + DW 104 ; Number of 128-byte records per track + DB 5 ; Block shift. 3 => 1k, 4 => 2k, 5 => 4k + DB 0x1f ; Block mask. 7 => 1k, 0Fh => 2k, 1Fh => 4k... + DB 00 ; Extent mask + DW 242 ; (no. of blocks on the disk)-1 + DW 0x7f ; (no. of directory entries)-1 + DB 128 ; Directory allocation bitmap, first byte + DB 0 ; Directory allocation bitmap, second byte + DW 0x0020 ; Checksum vector size, 0 for a fixed disk + ; No. directory entries/4, rounded up. + DB 2 ; Offset, number of reserved tracks + + ; Just garbage, I think... + DB $00,$20,$68,$01,$01,$82,$00,$1A,$00,$00,$00,$06,$0C,$12,$18,$04,$0A,$10,$16,$02,$08,$0E,$14,$01,$07,$0D,$13,$19,$05,$0B,$11,$17,$03,$09,$0F,$15 + + DB $32,$00 ;[0762] + ; DPB for disk 0, fake choice "6" + DW 26 ; Number of 128-byte records per track + DB 3 ; Block shift. 3 => 1k, 4 => 2k, 5 => 4k + DB 0x07 ; Block mask. 7 => 1k, 0Fh => 2k, 1Fh => 4k... + DB 00 ; Extent mask + DW 242 ; (no. of blocks on the disk)-1 + DW 0x3f ; (no. of directory entries)-1 + DB 192 ; Directory allocation bitmap, first byte + DB 0 ; Directory allocation bitmap, second byte + DW 0x0010 ; Checksum vector size, 0 for a fixed disk + ; No. directory entries/4, rounded up. + DB 2 ; Offset, number of reserved tracks + + ; Just garbage, I think... + DB $00,$08,$1a,00,$00,$82,$00,$1A,$00,$00,$00,$06,$0C,$12,$18,$04,$0A,$10,$16,$02,$08,$0E,$14,$01,$07,$0D,$13,$19,$05,$0B,$11,$17,$03,$09,$0F,$15,$3c + + ; inserted value when choosing SANCO model +bSelection: + DB $00 + + ; other garbage ;[0798] + DB $C0,$3A,$CE,$3D,$FE,$06,$DA,$5A,$2C,$3E,$06,$11,$CF,$3D,$21,$42,$3F,$47,$7E,$B7,$C2,$AF,$04,$1A,$77,$23,$13,$05,$C2,$66,$2C,$70,$C9,$CD,$B6,$2C,$CD,$C2,$0B,$FE,$2F,$C2,$97,$04,$CD,$4F,$0B,$C4,$AE,$2C,$0C,$12,$18,$04,$0A,$10,$16,$02,$08,$0E,$14,$01,$07,$0D,$13,$19,$05,$0B,$11,$17,$03,$09,$0F,$15,$32,$00,$1A,$00,$03,$07,$00,$F2,$00,$3F,$00,$C0,$00,$10,$00,$02,$00,$08,$1A,$00,$00,$82,$00,$1A,$00,$00,$00,$06,$0C,$12 diff --git a/applications/PAR8003.relocated.asm b/applications/PAR8003.relocated.asm new file mode 100644 index 0000000..b6a14b6 --- /dev/null +++ b/applications/PAR8003.relocated.asm @@ -0,0 +1,515 @@ +; Relocated code from PAR8003.COM. +; This code reimplements the rwfs routines. +; Actually, I copied the rwfs routines from the ROM and adapted them adding the +; missing lines. +; z80asm FUNK00.COM.asm -b -o FUNK00.COM + + org 0xbc00 + + ; FDC Read Write Format Seek routine. + ; Arguments: + ; - a: bytes per sector "shift factor", bps = 0x80 << a + ; - b: operation command, see switch in this routine + ; - c: drive number (0-3) + HD flag + ; - d: track number + ; - e: sector number + ; - hl: read/write buffer address +fdc_rwfs: + push bc + push de + push hl + ld hl,todo_wip + ld (hl),$00 + bit 7,a + jr z,label_03a6 + ld (hl),$01 +label_03a6: + res 7,a + ld ($ffb8),a ; bytes per sector + pop hl + push hl + ld a,$0a + ld ($ffbf),a ; value used in error checking routines + ld ($ffb9),bc ; *$ffb9 = drive no. + HD flag, *$ffba = operation command + ld ($ffbb),de ; *$ffbb = sector number, *$ffbc = track number + ld ($ffbd),hl ; base address for read/write buffer + call fdc_initialize_drive ; move head to track 0 if never done before on current drive + ld a,($ffba) ; load command byte + and $f0 ; take only the most significant nibble + jp z,fdc_sw_track0 ; case $00: move to track 0 (home) + cp $40 + jp z,fdc_sw_read_data ; case $40: read sector in hl buffer + cp $80 + jp z,fdc_sw_write_data ; case $80: write sector in hl buffer + cp $20 + jp z,fdc_sw_seek ; case $20: move head to desired track + cp $f0 + jp z,fdc_sw_format ; case $f0: format desired track + ld a,$ff + jp fdc_sw_default ; default: return -1 +fdc_sw_write_data: + call fdc_write_data + jr fdc_sw_default +fdc_sw_read_data: + call fdc_read_data + jr fdc_sw_default +fdc_sw_seek: + call fdc_seek + jr fdc_sw_default +fdc_sw_track0: + call fdc_track0 + jr fdc_sw_default +fdc_sw_format: + call fdc_format + jr fdc_sw_default +fdc_sw_default: + pop hl + pop de + pop bc + ret + + ; FDC write data routine + ; Writes data from *$ffbd to desired track/sector + ; Arguments are stored in $ffb8-$ffbf, as explained in caller fdc_rwfs +fdc_write_data: + call fdc_seek ; move to desired track +fdc_write_retry_c1f7: + call fdc_compute_bps ; compute bytes to be processed, result in de + push de + call fdc_wait_busy + ld c,$c5 ; load "write data" command with MT and MF flags set + ld a,($ffb8) + or a + jr nz,label_c208 ; if ssf = 0 (128 bytes per sector)... + res 6,c ; ...clear MF flag (FM mode) +label_c208: + call fdc_send_cmd ; send the "write data" command with desired MF flag + di ; disable interrupts + call fdc_send_rw_args ; send "write data" arguments (common with "read data" arguments) + pop de + ld c,$c1 ; prepare IO address in c + ld b,e ; load number of bytes to write (LSB) + ld hl,($ffbd) ; load base address of writing buffer + ; Buffer writing loop +label_c216: + in a,($82) ; wait until FDC is ready (INT = 1) + bit 2,a + jr z,label_c216 + in a,($c0) ; read FDC main status register + bit 5,a ; check if still in execution phase... + jr z,label_c229 ; ...if not, end writing + outi ; write data from buffer to FDC: IO(c) = *(hl++); b--; + jr nz,label_c216 + dec d + jr nz,label_c216 ; write ends when d = 0 and b = 0 + +label_c229: + out ($dc),a ; request Terminal Count to FDC + ei ; enable interrupts again + call fdc_rw_status ; read command response, put it in $ffc0-$ffc6 + ld a,($ffc0) ; fetch status (ST0) + and $c0 ; mask Interrupt Code bits (as in fdc_sis routine)... + cp $40 + jr nz,label_c248 ; ... and return if IC != 01 (!= "readfail") + call fdc_err_check ; after-write error checking (common with "read data") + ld a,($ffbf) ; keep a retry counter to avoid infinite loops + dec a ; decrement number of remaining retry + ld ($ffbf),a + jp nz,fdc_write_retry_c1f7 ; after 10 tries give up and... + ld a,$ff + ret ; ... return -1 +label_c248: + xor a + ret ; return 0 + + ; FDC read data routine + ; Read data from desired track/sector to *$ffbd + ; Arguments are stored in $ffb8-$ffbf, as explained in caller fdc_rwfs +fdc_read_data: + call fdc_seek ; move to desired track +fdc_read_retry: + call fdc_compute_bps ; compute bytes to be processed, result in de + push de + call fdc_wait_busy + ld c,$c6 ; load "read data" command with MT and MF flags set + ld a,($ffb8) + or a + jr nz,label_c25e ; if ssf = 0 (128 bytes per sector)... + res 6,c ; ...clear MF flag (FM mode) +label_c25e: + call fdc_send_cmd ; send the "read data" command + di ; disable interrupts + call fdc_send_rw_args ; send "read data" arguments (common with "write data" arguments) + pop de + ld c,$c1 ; prepare IO address in c + ld b,e ; load number of bytes to write (LSB) + ld hl,($ffbd) ; load base address of reading buffer +label_c26c: + in a,($82) ; wait until FDC is ready (INT = 1) + bit 2,a + jr z,label_c26c + in a,($c0) ; read FDC main status register + bit 5,a ; check if still in execution phase... + jr z,label_c27f ; ...if not, end reading + ini ; read data from FDC to *hl, hl++, b-- + jr nz,label_c26c + dec d + jr nz,label_c26c ; read ends when d = 0 and b = 0 + +label_c27f: + out ($dc),a ; request Terminal Count to FDC + ei ; enable interrupts again + call fdc_rw_status ; read command response, put it in $ffc0-$ffc6 + ld a,($ffc0) ; fetch status (ST0) + and $c0 ; mask Interrupt Code bits (as in fdc_sis routine)... + cp $40 + jr nz,label_c29e ;... and return if IC != 01 (!= "readfail") + call fdc_err_check ; after-write error checking (common with "write data") + ld a,($ffbf) ; keep a retry counter to avoid infinite loops + dec a ; decrement number of remaining retry + ld ($ffbf),a + jp nz,fdc_read_retry ; after 10 tries give up and... + ld a,$ff + ret ; ... return -1 +label_c29e: + xor a + ret ; return 0 + + ; FDC utility function: called only if read or write operation fails. + ; A head position reset (recalibrate) is issued if overrun or missing + ; address mark in data field event occur. +fdc_err_check: + ld a,($ffc2) ; read 2nd status register (ST1) + bit 4,a ; check OverRun bit (OR) + jr z,label_c2ab ; if not set, return, else... + call fdc_track0 ; ...reset head position... + ret ; ...and return for retry +label_c2ab: + ld a,($ffc1) ; read 3rd status register (ST2) + bit 0,a ; check Missing Address Mark in Data Field (MD) bit + jr z,label_c2b6 ; if not set, return, else... + call fdc_track0 ; ...reset head position... + ret ; ...and return for retry +label_c2b6: + ret ; return and just retry + + ; FDC utility function: compute number of bytes to be moved + ; Arguments: + ; - $ffb8: sector size factor (bps = 0x80 << ssf) + ; Note: if = 0, FM encoding is used. If != 0, MFM encoding is used + ; - $ffba: sector burst (bits 3:0) + ; - $ffbb: sector burst enabled (bit 7), valid only for ssf = 3 + ; Return: + ; - de: bytes to be processed + + ; | ssf | sbe = 0 | sbe = 1 | + ; | ---- | ------- | ---------------- | + ; | 0 | 128 + 256 * sb | + ; | 1 | 256 + 256 * sb | + ; | 2 | 256 + 256 * sb | + ; | 3 | 1024 | 1024 + 1024 * sb | +fdc_compute_bps: + ld e,$00 ; e = 0 + ld a,($ffb8) ; load ssf, valid values are 0 to 3 + cp $03 + jr nz,label_c2d4 ; handle separately ssf != 3 + ld d,$04 ; if ssf = 3 (bps = 1024) d = 4 + ld a,($ffbb) ; load sector burst enabled bit + bit 7,a + jr z,label_c2e2 ; if sbe = 0, just return (e = 0 (256), d = 4 --> 1024) + ld a,($ffba) ; load (lower nibble of) operation command + and $0f + rlca + rlca + add d + ld d,a ; return: d = (sb + 1) * 4 + jr label_c2e2 ; e = 0 --> 1024 + 1024 * sb + +label_c2d4: ; if bytes per sector != 1024... + or a + jr nz,label_c2d9 + ld e,$80 ; if ssf = 0, e = 128 +label_c2d9: + ld a,($ffba) ; load sector burst + and $0f + ld d,$01 + add d + ld d,a ; d = sb + 1 +label_c2e2: + ret + + ; Format floppy disk + ; Arguments are stored in $ffb8-$ffbf, as explained in caller fdc_rwfs + ; During format, ID fields are supplied to FDC, one for each sector in track. + ; Each ID field is 4 bytes long. +fdc_format: + call fdc_seek ; move to desired track + cp $ff ; if not able to locate track... + ret z ; ...return -1 + ld b,$14 ; if ssf=3, 5 sectors per track (5*4=20) + ld a,($ffb8) ; load sector size factor + cp $03 + jr z,label_c2f4 ; if less than 1024 bytes per sector... + ld b,$40 ; if ssf<3, 16 sectors per track (16*4=64) +label_c2f4: + push bc + call fdc_wait_busy + ld c,$4d + call fdc_send_cmd ; send "write id" command + ld bc,($ffb9) ; 1st argument: drive number (c <= *$ffb9) + call fdc_send_cmd + ld a,($ffb8) ; 2nd argument: sector size factor + ld c,a + call fdc_send_cmd + ld c,$05 ; if ssf = 3, sectors per track = 5 + ld a,($ffb8) ; laod ssf + cp $03 + jr z,label_c316 + ld c,$10 ; if *ssf != 3, sectors per track = 16 +label_c316: + call fdc_send_cmd ; 3rd argument: sectors per track + ld c,$28 ; gap length is 40 + call fdc_send_cmd ; 4rd argument: gap3 length + di ; disable interrupts + ld c,$e5 + call fdc_send_cmd ; 5th argument: filler byte value = 0xe5 + pop bc + ld c,$c1 ; prepare IO address in c + ld hl,($ffbd) ; prepare buffer address in hl +label_c32a: + in a,($82) ; wait until FDC is ready (INT = 1) + bit 2,a + jr z,label_c32a + in a,($c0) ; read main status register + bit 5,a ; check if still in execution phase... + jr z,label_c33a ; ...if not, end formatting + outi ; write sector IDs + jr nz,label_c32a +label_c33a: + out ($dc),a ; request Terminal Count to FDC + ei ; enable interrupts again + call fdc_rw_status ; command response, put it in $ffc0-$ffc6 + ld a,($ffc0) ; fetch status (ST0) + and $c0 ; mask Interrupt Code bits (as in fdc_sis routine)... + cp $40 + jr nz,label_c34c ; ... and return if IC != 01 (!= "readfail") + ld a,$ff + ret ; return -1 +label_c34c: + xor a + ret ; return 0 + + ; FDC utility function: send arguments for read or write data commands +fdc_send_rw_args: + ld bc,($ffb9) ; 1st argument: load drive number + call fdc_send_cmd + ld de,($ffbb) ; 2nd argument: track number + ld c,d ; track is in d <= *$ffbc + call fdc_send_cmd + ld bc,($ffb9) ; ffb9 contains HD flag too (physical head number) + ld a,c + and $04 ; extract bit 2 (HD) + rrca + rrca ; Move in bit 0 position + ld c,a + call fdc_send_cmd ; 3rd argument: head number (0/1) + res 7,e ; reset bit 7 in e (sector number register loaded before) + ld c,e + inc c ; hypothesys: sector number - 1 is stored in $ffbb + call fdc_send_cmd ; 4th argument: sector number to write + ld a,($ffb8) + ld c,a + call fdc_send_cmd ; 5th argument: bytes per sector "factor" + ld c,$05 ; default value for EOT = 5 + ld a,($ffb8) ; load bytes per sector "factor" + cp $03 + jr z,label_c383 ; if less than 1024 bytes per sector... + ld c,$10 ; override EOT with c = 16 +label_c383: + call fdc_send_cmd ; 6th argument: EOT - final sector of a track + ld c,$28 + call fdc_send_cmd ; 7th argument: GPL - gap length fixed to 0x28 + ld c,$ff + call fdc_send_cmd ; 8th argument: DTL - data length, should be invalid if 5th argument is != 0 + ret + + ; This routine seems to move the floppy head to track 0, then waits for the operation execution +fdc_track0: + call fdc_wait_busy + ld c,$07 ; Recalibrate command + call fdc_send_cmd ; Send command to FDC + ld bc,($ffb9) ; Load drive number? + res 2,c ; For some reason, clear bit 2. Recalibrate argument must be 0b000000xx, where xx = drive number in [0,3] + call fdc_send_cmd ; Send command to FDC + call fdc_sis ; send SIS to check if head movement was correctly completed + jr z,fdc_track0 ; check if return is "readfail" (Z = 0) then retry, else... + xor a ; ... return 0 + ret + + ; FDC: sends the seek command and move head upon desired track + ; Arguments: + ; - $ffbb: new track number + ; - $ffb9: drive number +fdc_seek: + ld de,($ffbb) ; load track number + ld a,d ; track number is in d <= *$ffbc + or a ; check if requested track is 0... + jp z,fdc_track0 ; if track = 0, skip this and call appropriate routine + call fdc_wait_busy + ld c,$0f ; send "seek" command + call fdc_send_cmd + ld bc,($ffb9) ; 1st arg: load and send *$ffb9 = drive number + HD flag + call fdc_send_cmd + ld hl,todo_wip + bit 0,(hl) + jr z,label_05d4 + sla d +label_05d4: + ld c,d ; 2nd arg: send NCN (new cylinder number) = desired track + call fdc_send_cmd + call fdc_sis ; sends SIS to check if head movement was correctly completed + jr nz,label_c3d0 ; if fdc_sis returns "OK" (Z != 0), return, else... + call fdc_track0 ; ...move head to track 0... + jp fdc_seek ; ...and try again +label_c3d0: + xor a ; On success, a=0 and return + ret + + ; FDC utility function: send "Sense Interrupt Status" command and read the two bytes (ST0, PCN) + ; Return: + ; - Z flag from the comparison (ST0 & 0xC0) == 0x40. + ; ST0[7:6] is Interrupt Code, and is: + ; - 00 if previous operation was successful (OK); + ; - 01 if previous operation was not successful (readfail); + ; - other cases are treated as successful. +fdc_sis: + in a,($82) ; wait until FDC is ready (INT = 1) + bit 2,a + jp z,fdc_sis + call fdc_wait_busy + call fdc_wait_rqm_wr ; wait until FDC is ready for write request + ld a,$08 ; send "Sense Interrupt Status" command + out ($c1),a + call fdc_wait_rqm_rd ; wait for data ready from FDC + in a,($c1) ; read status byte (ST0) + ld b,a + call fdc_wait_rqm_rd + in a,($c1) ; read present cylinder number (PCN), aka current track + ld a,b ; discard PCN + and $c0 + cp $40 ; perform (ST0 & 0xC0) == 0x40 + ret ; return is in Z flag + + ; FDC utility function: read response after read/write/format execution. + ; A 7 byte response is given, read it all in $ffc0-$ffc6 +fdc_rw_status: + ld hl,$ffc0 ; buffer pointer + ld b,$07 ; data length, answer is 7 byte long + ld c,$c1 ; IO address +label_c3fb: + call fdc_wait_rqm_rd ; wait until FDC is ready to send data + ini ; read from IO in *hl, hl++, b-- + jr nz,label_c3fb ; end if b = 0 + ret + + ; SUBROUTINE C403 ; wait for ioaddr(0xc0) to become "0b10xxxxxx" + ; FDC utility function: read main status register and wait for RQM = 1 and DIO = 0. + ; RQM = request from master, RQM = 1 means FDC is ready for communication with the CPU + ; DIO = data input/output, DIO = 0 means transfer from CPU to FDC +fdc_wait_rqm_wr: + in a,($c0) + rlca + jr nc,fdc_wait_rqm_wr ; while (bit7 == 0), try again + rlca + jr c,fdc_wait_rqm_wr ; while (bit7 == 1) && (bit6 == 1), try again + ret + + ; SUBROUTINE C40C ; wait for ioaddr(0xc0) to become "0b11xxxxxx" + ; FDC utility function: read main status register and wait for RQM = 1 and DIO = 1 + ; RQM = request from master, RQM = 1 means FDC is ready for communication with the CPU + ; DIO = data input/output, DIO = 1 means transfer from FDC to CPU +fdc_wait_rqm_rd: + in a,($c0) + rlca + jr nc,fdc_wait_rqm_rd ; while (bit7 == 0), try again + rlca + jr nc,fdc_wait_rqm_rd ; while (bit7 == 1) && (bit6 == 0), try again + ret + + ; SUBROUTINE C415 + ; FDC utility function: send a command byte to the FDC + ; Arguments: + ; - c: the command byte +fdc_send_cmd: + call fdc_wait_rqm_wr ; wait until FDC is ready to receive data + ld a,c + out ($c1),a ; actually send the comamnd + ret + + ; SUBROUTINE C41C ; while( ioaddr(0xc0).4 == 1 ), wait + ; FDC utility function: wait until the FDC is no more busy. + ; $C0 may be the main status register, where bit 4 is the CB (active high busy) flag. +fdc_wait_busy: + in a,($c0) + bit 4,a + jr nz,fdc_wait_busy + ret + + ; FDC drive initialization + ; Reset head position at least one time per drive since the computer was + ; turned on. + ; Arguments: + ; - c: drive number +fdc_initialize_drive: + ld b,$01 + ld a,c + and $03 ; mask drive number only + or a + jr z,label_c430 +label_c42b: + rlc b ; at the end of the cycle... + dec a ; ... b = 1 << (drive number) + jr nz,label_c42b +label_c430: + ld a,($ffc7) + ld c,a + and b ; if this drive was already initialized... + ret nz ; ...return doing nothing + ld a,c + or b ; else, mark this drive as initialized... + ld ($ffc7),a ; ...store this information in ram... + call fdc_track0 ; ...and perform initialization (aka move head to track 0) + ret + + ; SUBROUTINE C43F + ; FDC initialization. + ; Configure the FDC IC with: + ; - SRT = 6 (Step Rate Time = 6ms) + ; - HUT = F (Head Unload Time = 240ms) + ; - HLT = D (Head Load Time = 26ms) + ; - ND = 1 (DMA mode disabled) +fdc_init: + push bc + push hl + ld hl,fdc_cfg_base ; prepare HL to address FDC configuration table + call fdc_wait_busy + ld c,$03 ; send "specify" command + call fdc_send_cmd + ld c,(hl) ; load first "specify" argument from table + inc hl + call fdc_send_cmd ; send SRT | HUT + ld c,(hl) ; load second "specify" argument from table + call fdc_send_cmd ; send HLT | ND + xor a + ld ($ffc7),a ; *$ffc7 = 0 + pop hl + pop bc + ret + +todo_wip: + BYTE $00 + + ; STATIC DATA for C43F +fdc_cfg_base: + BYTE $6f ; SRT << 4 | HUT + BYTE $1b ; HLT << 1 | ND \ No newline at end of file diff --git a/applications/README.md b/applications/README.md new file mode 100644 index 0000000..786c313 --- /dev/null +++ b/applications/README.md @@ -0,0 +1,40 @@ + +# Applications + +## Custom Sanco applications + +- CONFIG80.COM: configuration of the serial port, **disassembly in progress**; +- [COPY8003.COM](COPY8003.COM.asm): disk copy utility; +- [FMT8003.COM](https://github.com/BayoDev/Sanco_8000/blob/main/CP-M/DISASSEMBLY/FMT8003_disassembly.z80): disk format utility; +- [FUNK00.COM](FUNK00.COM.asm): keymap configuration; +- [PAR8003.COM](PAR8003.COM.asm): configure diskette units; +- REV.COM: delete files, **disassembly in progress**; +- [RCX62.COM](https://github.com/BayoDev/Sanco_8000/blob/main/CP-M/DISASSEMBLY/RCX62_disassembly.z80): receive data from serial port; +- [SG8003.COM](SG8003.COM.asm): boot sector copier, used to change the boot program +- [SLF80037.COM](SLF80037.COM.asm): bootstrap application; +- [TERM80.COM](https://github.com/BayoDev/Sanco_8000/blob/main/CP-M/DISASSEMBLY/TERM80_disassembly.z80): serial terminal; +- TRX62.COM: send data through serial port, **disassembly in progress**; + +## CP/M system tools and applications + +All of these sources can be found in [cpm2-plm.zip](http://www.retroarchive.org/cpm/archive/unofficial/download/cpm2-plm.zip) from retroarchive.org. +Some of these sources are in in [PL/M](https://it.wikipedia.org/wiki/PL/M) language, the compiler can be found in [Digital Research Source Code](http://www.retroarchive.org/cpm/archive/unofficial/source.html) page. + +- ASM.COM: ASM80 assembler from Digital Research; +- DDT.COM: debugger from Digital Research; +- DUMP.COM: dump file content; +- LOAD.COM: from Digital Research; +- PIP.COM: move or copy files; +- STAT.COM: disk usage, character i/o management; +- SUBMIT.COM: system utility from Digital Research; +- XSUB.COM: TODO. + +## Third party applications + +- ED.COM: text editor; +- INSTALL.COM: Microsoft Install; +- KANABAS.COM: basic interpreter; +- KBAS75.COM: basic interpreter; +- KBASIC.COM: basic interpreter; +- MP.COM: Microsoft Multiplan; +- MBASIC.COM: basic interpreter; diff --git a/applications/SG8003.COM.asm b/applications/SG8003.COM.asm new file mode 100644 index 0000000..2aad0c2 --- /dev/null +++ b/applications/SG8003.COM.asm @@ -0,0 +1,254 @@ +; z80dasm 1.1.6 +; command line: z80dasm -t -a -l ../from-cpmls/sg8003.com + + org 00100h + +P_TERMCPM := $00 +C_WRITE := $02 +C_WRITESTR := $09 +C_READSTR := $0a + +SYSCALL := $0005 + +main: + ld sp,main ;[0100] + ld a,002h ;[0103] this is useless... + ld a,001h ;[0105] + ld (short_buffer),a ;[0107] + or 030h ;[010a] + ld (STR_WELCOME+17),a ;[010c] + ld de,STR_WELCOME ;[010f] + call BDOS_WRITESTR ;[0112] +souce_loop: + ld de,STR_SOUCE ;[0115] + call BDOS_WRITESTR ;[0118] + call BDOS_READSTR_SHORT ;[011b] + cp '.' ;[011e] + jp z,00000h ;[0120] exit + cp '\r' ;[0123] + jp z,set_dest_loop ;[0125] + cp '*' ;[0128] + jp z,set_dest_loop ;[012a] + res 5,a ;[012d] + cp 'A' ;[012f] + jp z,valid_drive ;[0131] + cp 'B' ;[0134] + jp nz,souce_loop ;[0136] +valid_drive: + ld (STR_RETURN+11),a ;[0139] Replace drive ID in string with the inserted one + and 003h ;[013c] + dec a ;[013e] Take 0 or 1 if A or B was inserted + ld (CUR_DRIVE),a ;[013f] +return_loop: + ld de,STR_RETURN ;[0142] + call BDOS_WRITESTR ;[0145] + call BDOS_READSTR_SHORT ;[0148] + cp '.' ;[014b] + jp z,souce_loop ;[014d] + cp '\r' ;[0150] + jp nz,return_loop ;[0152] + call FDC_READ_BOOT_TRACK ;[0155] +set_autostart_loop: + ld de,STR_AUTOSTART ;[0158] + call BDOS_WRITESTR ;[015b] + call BDOS_READSTR_SHORT ;[015e] + cp '.' ;[0161] + jp z,00000h ;[0163] + cp '\r' ;[0166] + jp z,set_dest_loop ;[0168] + res 5,a ;[016b] + cp 'N' ;[016d] + jp z,set_dest_loop ;[016f] + cp 'Y' ;[0172] + jp nz,set_autostart_loop ;[0174] + call set_autostart_cmd ;[0177] +set_dest_loop: + ld de,STR_DISTINATION ;[017a] + call BDOS_WRITESTR ;[017d] + call BDOS_READSTR_SHORT ;[0180] + cp '.' ;[0183] + jp z,00000h ;[0185] + cp '\r' ;[0188] + jp z,BDOS_TERMCPM ;[018a] + res 5,a ;[018d] + cp 'A' ;[018f] + jp z,set_dest ;[0191] + cp 'B' ;[0194] + jp z,set_dest ;[0196] + jp set_dest_loop ;[0199] +set_dest: + ld (STR_DISTINATION_ON+17),a ;[019c] update destination drive letter in message + and 003h ;[019f] + dec a ;[01a1] + ld (CUR_DRIVE),a ;[01a2] +dest_on_loop: + ld de,STR_DISTINATION_ON ;[01a5] + call BDOS_WRITESTR ;[01a8] + call BDOS_READSTR_SHORT ;[01ab] + cp 003h ;[01ae] + jp z,00000h ;[01b0] + cp '\r' ;[01b3] + jp nz,dest_on_loop ;[01b5] + call FDC_WRITE_BOOT_TRACK ;[01b8] + jp set_dest_loop ;[01bb] + +BDOS_TERMCPM: + ld c,P_TERMCPM ;[01be] + jp SYSCALL ;[01c0] + +STR_WELCOME: + DB "\r\n" + DB "SG8003 Version =.0" + DB "$" + +; [sic] +STR_SOUCE: + DB "\r\n" + DB "SOUCE DRIVE NAME ? " + DB "$" + +; [sic] +STR_RETURN: + DB "\r\n" + DB "SOUCE ON \x00 THEN TYPE RETURN" + DB "$" + +; [sic] +STR_DISTINATION: + DB "\r\n" + DB "DISTINATION DRIVE NAME ? " + DB "$" + +; [sic] +STR_DISTINATION_ON: + DB "\r\n" + DB "DISTINATION ON > THEN TYPE RETURN" + DB "$" + +STR_AUTOSTART: + DB "\r\n" + DB "IF SET AUTO START THEN \"Y\" ELSE RETURN " + DB "$" + +STR_AUTOSTART_CMD: + DB "\r\n" + DB "SET AUTO START COMMAND " + DB "$" + +CUR_DRIVE: + DB 0 + +; Load boot tracks in memory +; TODO don't understand how much +FDC_READ_BOOT_TRACK: + ld b,040h ;[0291] Read command + call FDC_SETUP_PARAMS ;[0293] + ret ;[0296] + +FDC_WRITE_BOOT_TRACK: + ld b,080h ;[0297] Write command + call FDC_SETUP_PARAMS ;[0299] + ret ;[029c] + +; Setup common parameters to do things with boot tracks of the disk, +; like rw buffer, track and side indexes, and sector size factor value +FDC_SETUP_PARAMS: +; Load "loader" (sector 0 side 0 track 0) + ld a,(CUR_DRIVE) ;[029d] + ld c,a ;[02a0] + ld de,0000h ;[02a1] + ld hl,0900h ;[02a4] Destination buffer + ld a,001h ;[02a7] 256 bytes per sector (ssf) + call ROM_FDC_RWFS ;[02a9] +; Load entire side 1 track 0 + ld a,04h ;[02ac] Sector burst size = 4 + add a,b ;[02ae] Change only sector burst size without affecting command + ld b,a ;[02af] + ld a,(CUR_DRIVE) ;[02b0] + ld c,a ;[02b3] + set 2,c ;[02b4] set head=1 + ld d,000h ;[02b6] + ld e,000h ;[02b8] + set 7,e ;[02ba] SBE=1 + ld hl,00980h ;[02bc] Note: part of the loader's sector is overwritten. But loader is less than 128 bytes + ld a,003h ;[02bf] 1024 bytes per sector (ssf) +; Load sectors 1-15 from side 0 track 0 + call ROM_FDC_RWFS ;[02c1] + ld a,00ah ;[02c4] sector burst size = 4 + 10 = 14 + add a,b ;[02c6] + ld b,a ;[02c7] + ld a,(CUR_DRIVE) ;[02c8] + ld c,a ;[02cb] + ld d,000h ;[02cc] + ld e,001h ;[02ce] + ld hl,01d80h ;[02d0] + ld a,001h ;[02d3] + call ROM_FDC_RWFS ;[02d5] + ret ;[02d8] + +ROM_FDC_RWFS: + push af ;[02d9] + call 0ffa3h ;[02da] + call 0c018h ;[02dd] + call 0ffa6h ;[02e0] + cp 0ffh ;[02e3] + jr nz,ROM_FDC_RWFS_EPILOGUE ;[02e5] + pop af ;[02e7] + jr ROM_FDC_RWFS ;[02e8] +ROM_FDC_RWFS_EPILOGUE: + pop af ;[02ea] + ret ;[02eb] + +BDOS_READSTR_SHORT: + ld c,C_READSTR ;[02ec] + ld de,short_buffer ;[02ee] + call SYSCALL ;[02f1] + ld a,(short_buffer+1) ;[02f4] + or a ;[02f7] + jr z,empty_buffer ;[02f8] + ld a,(short_buffer+2) ;[02fa] + ret ;[02fd] +empty_buffer: + ld a,'\r' ;[02fe] default return value in case of empty buffer + ret ;[0300] + +short_buffer: + DB 0 ; Buffer size + DB 0 ; Enterd char len + DB 0,0 ; Actual data buffer + +BDOS_WRITESTR: + ld c,C_WRITESTR ;[0305] + call SYSCALL ;[0307] + ret ;[030a] + +BDOS_READSTR_LONG: + ld de,long_buffer ;[030b] + ld c,C_READSTR ;[030e] + call SYSCALL ;[0310] + ret ;[0313] + +; TODO Set autostart? +set_autostart_cmd: + ld de,STR_AUTOSTART_CMD ;[0314] + call BDOS_WRITESTR ;[0317] + call BDOS_READSTR_LONG ;[031a] + ld hl,long_buffer+1 ;[031d] copy the autostart command + ld de,00987h ;[0320] TODO where? + ld bc,16+1 ;[0323] string length + actual string + ldir ;[0326] + ret ;[0328] + +long_buffer: + DB 16 ;Buffer size + DB "\xfe" ;Enterd char len + REPT 16 ;Actual data buffer + DB 0 + ENDR + +; junk, just a copy of a piece of this program + DB "\xFB\x21\x80\x09\x3E\x03\xCD\xD9\x02\x3E\x0A\x80\x47\x3A\x90\x02" + DB "\x4F\x16\x00\x1E\x01\x21\x80\x1D\x3E\x01\xCD\xD9\x02\xC9\xF5\xCD" + DB "\xA3\xFF\xCD\x18\xC0\xCD\xA6\xFF\xFE\xFF\x20\x03\xF1\x18\xEF\xF1" + DB "\xC9\x0E\x0A\x11\x01\x03\xCD\x05\x00\x3A\x02\x03\xB7\x28\x04\x3A" + DB "\x03\x03\xC9\x3E\x0D" diff --git a/applications/SLF80037.COM.asm b/applications/SLF80037.COM.asm new file mode 100644 index 0000000..90aaa9c --- /dev/null +++ b/applications/SLF80037.COM.asm @@ -0,0 +1,908 @@ +; SLF8003, something like an AUTOEXEC.BAT for Sanco computer. +; z80asm SLF8003.COM.asm -b -o SLF8003.COM + + org 0x0100 + +;; Functions +SYSCALL: = $0005 + +;; CP/M loaded program starts from here + jp entrypoint ;[0100] + +comm_sio_cfg: ;[0103] + DB $00 ; Register 0 + DB $1b ; Channel reset? but with another register pointer (which is forbidden by the manual) + DB $00 ; Register 0 + DB $10 ; Reset interrupts + DB $00 ; Register 0 + DB $10 ; Reset interrupts + DB $04 ; Register 4 + DB $4c ; 2 stop bits + x16 clock mode + DB $01 ; Register 1 + DB $00 ; no interrupts + Db $03 ; Register 3 + DB $e1 ; Rx enable + Auto enables + 8 bit per character (Rx) + DB $05 ; Register 5 + DB $68 ; Tx enable + 8 bit per character (Tx) + +timer12_cfg: ;[0111] + DB $05, $01, $41 ; Timer 1: time constant 1, counter mode, no irq + DB $05, $10, $41 ; Timer 2: time constant 16, counter mode, no irq + DB $00 + + ;[0118] + DB $00,$00,$00 + +iobyte_init: ;[011b] + DB $80 + + ;[011c] + DB $00,$00,$00,$00,$00,$00,$00,$00,$00,$00 + DB $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$e0,$80,$e3,$03 + DB $e3,$05,$e3,$01,$e3,$c1,$ff,$cb,$00,$00,$00,$00,$80,$00 + DB $cd,$30,$01,$0e,$09,$cd,$05,$00,$c1,$21,$07,$00,$7e,$3d + DB $90 + ; Stack grows from here, so this is meaningless data that + ; will be overwritten. Just here for coherence with the + ; binary + + ; The following code is unreachable, so it is at the moment + ; ignored and will be disassembled in second place. + ; Kept just for coherence with binary file. +unreach0: + DB $57,$1E,$00,$D5,$21,$00,$02,$78,$B1,$CA,$65,$01,$0B,$7E + DB $12,$13,$23,$C3,$58,$01,$D1,$C1,$E5,$62,$78,$B1,$CA,$87 + DB $01,$0B,$7B,$E6,$07,$C2,$7A,$01,$E3,$7E,$23,$E3,$6F,$7D + DB $17,$6F,$D2,$83,$01,$1A,$84,$12,$13,$C3,$69,$01,$D1,$2E + DB $00,$E9,$0E,$10,$CD,$05,$00,$32,$5F,$1E,$C9,$21,$66,$1E + DB $70,$2B,$71,$2A,$65,$1E,$EB,$0E,$11,$CD,$05,$00,$32,$5F + DB $1E,$C9,$11,$00,$00,$0E,$12,$CD,$05,$00,$32,$5F,$1E,$C9 + DB $21,$68,$1E,$70,$2B,$71,$2A,$67,$1E,$EB,$0E,$13,$CD,$05 + DB $00,$C9,$21,$6A,$1E,$70,$2B,$71,$2A,$69,$1E,$EB,$0E,$14 + DB $CD,$05,$00,$C9,$21,$6C,$1E,$70,$2B,$71,$2A,$6B,$1E,$EB + DB $0E,$15,$CD,$05,$00,$C9,$21,$6E,$1E,$70,$2B,$71,$2A,$6D + DB $1E,$EB,$0E,$16,$CD,$05,$00,$32,$5F,$1E,$C9,$21,$70,$1E + DB $70,$4A,$01,$00,$C6,$B4,$13 + +welcome_str: ;[0200] + DB "\x1B\x46\x1A\x1B\x5F\x30\x1B\x5F\x34\x0D\x0A\x1B\x4F\x1B\x47\x1B\x41\x1B\x3D" + DB "**\x1b\x578003\x1b\x42 CP/M 2.2 vers 1.7FR\x1B\x58\x0D\x0A$" + + ;[0236] + DB "s 2.2\x0D\x0A\x09 BIOS vers 1.0\x0D\x0A\x09" + DB "Initial device assignment\x0D\x0A\x09" + DB " CON:=TTY: RDR:=TTY: PUN:=TTY: LST:=LPT:\x0D\x0A\x09" + DB "Iobyte Assignment\x0D\x0A\x09" + DB " CON:\x09\x09\x09\x09 LST:\x0D\x0A\x09 TTY: Key Buffer Off\x09" + DB " TTY: No Display\x0D\x0A\x09 CRT: Serial I/O\x09\x09" + DB " CRT: CRT Display\x0D\x0A\x09 BAT: Key Buffer Off\x09" + DB " LPT: Parallel Printer\x0D\x0A\x09 UC1: Key Buffer On\x09\x09" + DB " UL1: Serial Printer\x0D\x0A\x0A\x09 FRANCE KEY BOARD\x0D\x0A\x0A" + DB "\x1BA\x09\x09Bon jour!\x0D\x0A\x0A\x1BB$" + +unreach1: + DB $31,$00,$CD,$93,$00,$47,$0E,$40,$CD,$DB,$00,$B1,$B0,$C3 + DB $51,$01,$0E,$08,$21,$0A,$06,$CD,$B7,$00,$C2,$DC,$01,$0D + DB $79,$CD,$93,$00,$47,$0E,$80,$C3,$BE,$01,$0E,$02,$21,$12 + DB $06,$CD,$B7,$00,$C2,$F4,$01,$0C,$0C,$0C,$CD,$DB,$00,$CD + DB $93,$00,$B1,$C3,$51,$01,$0E,$01,$21,$16,$06,$CD,$B7,$00 + DB $C2,$10,$02,$CD,$DB,$00,$CD,$93,$00,$F6,$06,$CD,$51,$01 + DB $CD,$8C,$00,$C3,$51,$01,$0E,$06,$21,$2E,$06,$CD,$B7,$00 + DB $C2,$36,$02,$79,$FE,$04,$DA,$23,$02,$C6,$05,$47,$CD,$07 + DB $01,$CD,$99,$00,$B0,$CD,$51,$01,$E6,$CF,$FE,$01,$C0,$C3 + DB $A0,$01,$0E,$01,$21,$32,$06,$CD,$B7,$00,$C2,$51,$02,$CD + DB $8C,$00,$FE,$08,$D2,$18,$05,$CD,$93,$00,$F6,$C7,$C3,$51 + DB $01,$0E,$02,$21,$3E,$06,$CD,$B7,$00,$C2,$71,$02,$0D,$C2 + DB $65,$02,$0E,$C1,$C3,$67,$02,$0E,$C5,$CD,$10,$01,$CD,$99 + DB $00,$B1,$C3,$51,$01,$3A,$7A,$06,$FE,$4A,$C2,$81,$02,$CD + DB $45,$01,$F6,$02,$C3,$8B,$02,$FE,$43,$C2,$96,$02,$CD,$45 + DB $01,$F6,$04,$CD,$51,$01,$79,$CD,$51,$01,$78,$C3,$51,$01 + DB $FE,$52,$C2,$18,$05,$CD,$1D,$01,$F6,$C0,$C3,$51,$01,$2A + DB $0E,$00,$D5,$EB,$2A,$0C,$00,$7B,$95,$7A,$9C,$D2,$B7,$02 + DB $2A,$13,$00,$F9,$C9,$D1,$7E,$23,$22,$0C,$00,$C9,$3C,$E6 + DB $07,$FE,$06,$DA,$C8,$02,$C6,$03,$FE,$05,$DA,$CF,$02,$C6 + DB $02,$C6,$41,$4F,$C3,$15,$00,$47,$E6,$F0,$0F,$0F,$0F,$0F + DB $C6,$90,$27,$CE,$40,$27,$4F,$CD,$15,$00,$78,$E6,$0F,$C6 + DB $90,$27,$CE,$40,$27,$4F,$C3,$15,$00,$06,$04,$4E,$CD,$15 + DB $00,$23,$05,$C2,$F5,$02,$0E,$20,$C3,$15,$00,$7A,$E6,$38 + DB $0F,$0F,$0F,$C9,$CD,$03,$03,$87,$4F,$21,$42,$06,$09,$4E + DB $CD,$15,$00,$23,$4E,$CD,$15,$00,$0E,$20,$CD,$15,$00,$C3 + DB $15,$00,$CD,$03,$03,$E6,$06,$FE,$06,$C2,$BE,$02,$0E,$53 + DB $CD,$15,$00,$0E,$50,$C3,$15,$00,$CD,$2E,$00,$2A,$0C,$00 + DB $7C,$CD,$D5,$02,$7D,$CD,$D5,$02,$0E,$20,$CD,$15,$00,$CD + DB $15,$00,$C9,$21,$00,$00,$39,$22,$13,$00,$3A,$10,$00,$B7 + DB $CA,$71,$03,$21,$FF,$FF,$22,$0E,$00,$3C,$C2,$71,$03,$3C + DB $32,$10,$00,$2A,$0C,$00,$C3,$97,$03,$CD,$9E,$06,$C2,$40 + DB $05,$21,$10,$00,$7E,$B7,$CA,$83,$03,$35,$CA,$40,$05,$2A + DB $0C,$00,$CD,$A1,$06,$CD,$2E,$00,$0E,$20,$CD,$15,$00,$CD + DB $15,$00,$CD,$3B,$03,$CD,$A3,$02,$57,$21,$45,$05,$01,$11 + DB $00,$BE,$CA,$FD,$04,$23,$0D,$C2,$A1,$03,$0E,$0A,$BE,$CA + DB $E9,$04,$23,$0D,$C2,$AC,$03,$0E,$06,$BE,$CA,$CE,$04,$23 + DB $0D,$C2,$B7,$03,$E6,$C0,$FE,$40,$CA,$B4,$04,$FE,$80,$CA + DB $A5,$04,$7A,$E6,$C7,$D6,$04,$CA,$96,$04,$3D,$CA,$90,$04 + DB $3D,$CA,$7C,$04,$7A,$E6,$C0,$CA,$4A,$04,$7A,$E6,$07,$CA + DB $3F,$04,$D6,$02,$CA,$34,$04,$D6,$02,$CA,$29,$04,$D6,$03 + DB $CA,$1A,$04,$7A,$E6,$08,$C2,$0B,$05,$7A,$E6,$07,$4F,$3D + DB $21,$39,$06,$09,$CD,$F3,$02,$CD,$03,$03,$FE,$06,$C2,$9F + DB $04,$21,$36,$06,$CD,$F3,$02,$C3,$71,$03,$21,$32,$06,$CD + DB $F3,$02,$CD,$03,$03,$CD,$D5,$02,$C3,$71,$03,$0E,$43,$CD + DB $15,$00,$CD,$0A,$03,$C3,$D9,$04,$0E,$4A,$CD,$15,$00,$CD + DB $0A,$03,$C3,$D9,$04,$0E,$52,$CD,$15,$00,$CD,$0A,$03,$C3 + DB $71,$03,$21,$1A,$06,$7A,$E6,$07,$CA,$0B,$05,$7A,$E6,$0F + DB $3D,$CA,$6E,$04,$FE,$03,$DA,$61,$04,$D6,$05,$87,$87,$4F + DB $09,$CD,$F3,$02,$CD,$24,$03,$C3,$71,$03,$CD,$F3,$02,$CD + DB $24,$03,$0E,$2C,$CD,$15,$00,$C3,$D9,$04,$21,$16,$06,$CD + DB $F3,$02,$CD,$03,$03,$CD,$BE,$02,$0E,$2C,$CD,$15,$00,$C3 + DB $F4,$04,$21,$12,$06,$C3,$99,$04,$21,$0E,$06,$CD,$F3,$02 + DB $CD,$03,$03,$CD,$BE,$02,$C3,$71,$03,$7A,$E6,$38,$0F,$4F + DB $21,$EE,$05,$09,$CD,$F3,$02,$C3,$C5,$04,$21,$EA,$05,$CD + DB $F3,$02,$CD,$03,$03,$CD,$BE,$02,$0E,$2C,$CD,$15,$00,$7A + DB $E6,$07,$CD,$BE,$02,$C3,$71,$03,$79,$87,$87,$4F,$21,$CE + DB $05,$09,$CD,$F3,$02,$CD,$A3,$02,$F5,$CD,$A3,$02,$57,$F1 + DB $5F,$CD,$95,$06,$C3,$71,$03,$79,$87,$87,$4F,$21,$A6,$05 + DB $09,$CD,$F3,$02,$CD,$A3,$02,$CD,$92,$06,$C3,$71,$03,$79 + DB $87,$87,$4F,$21,$62,$05,$09,$CD,$F3,$02,$C3,$71,$03,$21 + DB $76,$06,$CD,$F3,$02,$7A,$CD,$92,$06,$C3,$71,$03,$CD,$2E + DB $00,$0E,$3F,$CD,$15,$00,$2A,$13,$00,$F9,$21,$00,$00,$39 + DB $22,$13,$00,$CD,$38,$03,$22,$11,$00,$CD,$89,$06,$CD,$5A + DB $01,$2A,$11,$00,$22,$0C,$00,$C3,$2B,$05,$2A,$13,$00,$F9 + DB $C9,$00,$07,$0F,$17,$1F,$27,$2F,$37,$3F,$76,$C9,$E3,$E9 + DB $EB,$F3,$F9,$FB,$C6,$CE,$D3,$D6,$DB,$DE,$E6,$EE,$F6,$FE + DB $22,$2A,$32,$3A,$C3,$CD,$45,$49,$20,$20,$53,$50,$48,$4C + DB $44,$49,$20,$20,$58,$43,$48,$47,$50,$43,$48,$4C,$58,$54 + DB $48,$4C,$52,$45,$54,$20,$48,$4C,$54,$20,$43,$4D,$43,$20 + DB $53,$54,$43,$20,$43,$4D,$41,$20,$44,$41,$41,$20,$52,$41 + DB $52,$20,$52,$41,$4C,$20,$52,$52,$43,$20,$52,$4C,$43,$20 + DB $4E,$4F,$50,$20,$43,$50,$49,$20,$4F,$52,$49,$20,$58,$52 + DB $49,$20,$41,$4E,$49,$20,$53,$42,$49,$20,$49,$4E,$20,$20 + DB $53,$55,$49,$20,$4F,$55,$54,$20,$41,$43,$49,$20,$41,$44 + DB $49,$20,$43,$41,$4C,$4C,$4A,$4D,$50,$20,$4C,$44,$41,$20 + DB $53,$54,$41,$20,$4C,$48,$4C,$44,$53,$48,$4C,$44,$4D,$4F + DB $56,$20,$41,$44,$44,$20,$41,$44,$43,$20,$53,$55,$42,$20 + DB $53,$42,$42,$20,$41,$4E,$41,$20,$58,$52,$41,$20,$4F,$52 + DB $41,$20,$43,$4D,$50,$20,$49,$4E,$52,$20,$44,$43,$52,$20 + DB $4D,$56,$49,$20,$4C,$58,$49,$20,$53,$54,$41,$58,$49,$4E + DB $58,$20,$44,$41,$44,$20,$4C,$44,$41,$58,$44,$43,$58,$20 + DB $52,$53,$54,$20,$50,$53,$57,$20,$50,$4F,$50,$20,$50,$55 + DB $53,$48,$4E,$5A,$5A,$20,$4E,$43,$43,$20,$50,$4F,$50,$45 + DB $50,$20,$4D,$20,$42,$20,$43,$20,$44,$20,$45,$20,$48,$20 + DB $4C,$20,$4D,$20,$41,$20,$42,$20,$20,$20,$44,$20,$20,$20 + DB $48,$20,$20,$20,$53,$50,$20,$20,$50,$53,$57,$20,$3F,$3F + DB $3D,$20,$1E,$4D,$06,$00,$21,$45,$C3,$A2,$06,$C3,$AA,$06 + DB $C3,$CF,$0D,$C3,$B6,$0B,$C3,$DD,$0B,$C3,$C7,$0B,$C3,$05 + DB $0C,$C3,$2D,$0C,$C3,$90,$0C,$C3,$66,$0C,$C3,$1F,$0C,$C9 + DB $E3,$22,$4A,$0F,$E3,$C3,$00,$00,$2A,$06,$00,$22,$A8,$06 + DB $21,$A2,$06,$22,$01,$00,$21,$00,$00,$22,$06,$00,$AF,$32 + DB $4F,$0F,$21,$00,$01,$22,$0C,$00,$22,$5D,$0F,$22,$87,$0F + DB $22,$B9,$0F,$21,$00,$01,$31,$B7,$0F,$E5,$21,$02,$00,$E5 + DB $2B,$2B,$22,$B7,$0F,$E5,$E5,$22,$4D,$0F,$3E,$C3,$32,$38 + DB $00,$21,$86,$06,$22,$39,$00,$3A,$5D,$00,$FE,$20,$CA,$FE + DB $06,$21,$00,$00,$E5,$C3,$AD,$09,$31,$AF,$0F,$CD,$93,$09 + DB $DA,$0D,$07,$21,$80,$06,$22,$06,$00,$CD,$15,$0C,$3E,$2D + DB $CD,$C7,$0B,$CD,$B6,$0B,$CD,$DD,$0B,$FE,$0D,$CA,$FE,$06 + DB $D6,$41,$DA,$AB,$0B,$FE,$1A,$D2,$AB,$0B,$5F,$16,$00,$21 + DB $37,$07,$19,$19,$5E,$23,$56,$EB,$E9,$7E,$07,$AB,$0B,$AB + DB $0B,$C6,$07,$AB,$0B,$5C,$08,$70,$08,$DA,$08,$04,$09,$AB + DB $0B,$AB,$0B,$97,$07,$5A,$09,$AB,$0B,$AB,$0B,$AB,$0B,$AB + DB $0B,$9C,$09,$7A,$0A,$C3,$0A,$BF,$0A,$AB,$0B,$AB,$0B,$E7 + DB $0A,$AB,$0B,$AB,$0B,$E5,$D5,$C5,$AF,$32,$5B,$00,$0E,$0F + DB $11,$5C,$00,$CD,$A2,$06,$C1,$D1,$E1,$C9,$CD,$93,$09,$D2 + DB $AB,$0B,$CD,$90,$0C,$3D,$C2,$AB,$0B,$CD,$66,$0C,$22,$0C + DB $00,$CD,$09,$00,$C3,$FE,$06,$CD,$93,$09,$D2,$AB,$0B,$CD + DB $90,$0C,$CA,$BB,$07,$CD,$66,$0C,$22,$0C,$00,$3D,$CA,$BB + DB $07,$CD,$66,$0C,$22,$0E,$00,$3D,$C2,$AB,$0B,$AF,$C3,$BD + DB $07,$3E,$0C,$32,$10,$00,$CD,$06,$00,$C3,$FE,$06,$CD,$90 + DB $0C,$CA,$E5,$07,$CD,$66,$0C,$DA,$D5,$07,$22,$5D,$0F,$E6 + DB $7F,$3D,$CA,$E5,$07,$CD,$66,$0C,$3D,$C2,$AB,$0B,$C3,$F0 + DB $07,$2A,$5D,$0F,$7D,$E6,$F0,$6F,$11,$BF,$00,$19,$22,$5F + DB $0F,$CD,$15,$0C,$CD,$1F,$0C,$C2,$FE,$06,$2A,$5D,$0F,$22 + DB $61,$0F,$CD,$2E,$0C,$CD,$C5,$0B,$7E,$CD,$05,$0C,$23,$CD + DB $45,$0C,$DA,$19,$08,$7D,$E6,$0F,$C2,$05,$08,$22,$5D,$0F + DB $2A,$61,$0F,$EB,$CD,$C5,$0B,$1A,$CD,$36,$0C,$13,$2A,$5D + DB $0F,$7D,$93,$C2,$23,$08,$7C,$92,$C2,$23,$08,$2A,$5D,$0F + DB $CD,$45,$0C,$DA,$FE,$06,$C3,$F3,$07,$CD,$90,$0C,$FE,$03 + DB $C2,$AB,$0B,$CD,$66,$0C,$E5,$CD,$66,$0C,$E5,$CD,$66,$0C + DB $D1,$C1,$C9,$7B,$91,$7A,$98,$C9,$CD,$41,$08,$7C,$B7,$C2 + DB $AB,$0B,$CD,$57,$08,$DA,$FE,$06,$7D,$02,$03,$C3,$64,$08 + DB $CD,$15,$0C,$CD,$90,$0C,$CD,$66,$0C,$E5,$CD,$66,$0C,$E5 + DB $CD,$66,$0C,$44,$4D,$D1,$E1,$F3,$CA,$A1,$08,$DA,$8F,$08 + DB $22,$B9,$0F,$E6,$7F,$3D,$CA,$A1,$08,$CD,$B2,$08,$3D,$CA + DB $A1,$08,$59,$50,$CD,$B2,$08,$31,$AF,$0F,$D1,$C1,$F1,$E1 + DB $F9,$2A,$B9,$0F,$E5,$2A,$B7,$0F,$FB,$C9,$F5,$C5,$21,$4F + DB $0F,$7E,$34,$B7,$CA,$CD,$08,$23,$7E,$23,$46,$23,$BB,$C2 + DB $CD,$08,$78,$BA,$C2,$CD,$08,$7E,$12,$23,$73,$23,$72,$23 + DB $1A,$77,$3E,$FF,$12,$C1,$F1,$C9,$CD,$90,$0C,$FE,$02,$C2 + DB $AB,$0B,$CD,$66,$0C,$E5,$CD,$66,$0C,$D1,$E5,$CD,$15,$0C + DB $19,$CD,$2E,$0C,$CD,$C5,$0B,$E1,$AF,$95,$6F,$3E,$00,$9C + DB $67,$19,$CD,$2E,$0C,$C3,$FE,$06,$AF,$32,$7C,$00,$32,$5C + DB $00,$CD,$DD,$0B,$0E,$09,$21,$5D,$00,$77,$23,$0D,$CA,$AB + DB $0B,$CD,$DD,$0B,$FE,$2E,$CA,$26,$09,$FE,$0D,$C2,$13,$09 + DB $0D,$CA,$30,$09,$36,$20,$23,$C3,$26,$09,$0E,$04,$FE,$2E + DB $C2,$4B,$09,$21,$65,$00,$CD,$DD,$0B,$FE,$0D,$CA,$4B,$09 + DB $77,$23,$0D,$CA,$AB,$0B,$C3,$3A,$09,$0D,$CA,$55,$09,$36 + DB $20,$23,$C3,$4B,$09,$36,$00,$C3,$FE,$06,$CD,$41,$08,$CD + DB $57,$08,$DA,$FE,$06,$0A,$03,$77,$23,$C3,$5D,$09,$21,$65 + DB $00,$7E,$E6,$7F,$FE,$48,$C0,$23,$7E,$E6,$7F,$FE,$45,$C0 + DB $23,$7E,$E6,$7F,$FE,$58,$C9,$EB,$2A,$87,$0F,$7D,$93,$7C + DB $9A,$EB,$C9,$CD,$81,$09,$D0,$22,$87,$0F,$C9,$E5,$21,$00 + DB $00,$CD,$81,$09,$E1,$C9,$CD,$90,$0C,$21,$00,$00,$CA,$AC + DB $09,$3D,$C2,$AB,$0B,$CD,$66,$0C,$E5,$CD,$6B,$07,$FE,$FF + DB $CA,$AB,$0B,$CD,$6A,$09,$CA,$E1,$09,$E1,$11,$00,$01,$19 + DB $E5,$11,$5C,$00,$0E,$14,$CD,$A2,$06,$E1,$B7,$C2,$46,$0A + DB $11,$80,$00,$0E,$80,$1A,$13,$77,$23,$0D,$C2,$D3,$09,$CD + DB $8B,$09,$C3,$C0,$09,$CD,$74,$0B,$FE,$1A,$CA,$AB,$0B,$DE + DB $3A,$C2,$E1,$09,$57,$E1,$E5,$CD,$26,$0A,$5F,$CD,$26,$0A + DB $F5,$CD,$26,$0A,$C1,$4F,$09,$7B,$B7,$C2,$0C,$0A,$60,$69 + DB $22,$B9,$0F,$C3,$46,$0A,$CD,$26,$0A,$CD,$26,$0A,$77,$23 + DB $1D,$C2,$0F,$0A,$CD,$26,$0A,$F5,$CD,$8B,$09,$F1,$C2,$AB + DB $0B,$C3,$E1,$09,$C5,$E5,$D5,$CD,$74,$0B,$CD,$59,$0C,$07 + DB $07,$07,$07,$E6,$F0,$F5,$CD,$74,$0B,$CD,$59,$0C,$C1,$B0 + DB $47,$D1,$82,$57,$78,$E1,$C1,$C9,$0E,$0C,$CD,$A2,$06,$21 + DB $6F,$0A,$7E,$B7,$CA,$5A,$0A,$CD,$C7,$0B,$23,$C3,$4E,$0A + DB $CD,$15,$0C,$2A,$87,$0F,$CD,$2E,$0C,$CD,$C5,$0B,$2A,$B9 + DB $0F,$CD,$2E,$0C,$C3,$FE,$06,$0D,$0A,$4E,$45,$58,$54,$20 + DB $20,$50,$43,$00,$CD,$90,$0C,$3D,$C2,$AB,$0B,$CD,$66,$0C + DB $CD,$15,$0C,$E5,$CD,$2E,$0C,$CD,$C5,$0B,$E1,$7E,$E5,$CD + DB $05,$0C,$CD,$C5,$0B,$CD,$B6,$0B,$CD,$DD,$0B,$E1,$FE,$0D + DB $CA,$BB,$0A,$FE,$2E,$CA,$FE,$06,$E5,$CD,$93,$0C,$3D,$C2 + DB $AB,$0B,$CD,$66,$0C,$7C,$B7,$C2,$AB,$0B,$7D,$E1,$77,$23 + DB $C3,$84,$0A,$AF,$C3,$C5,$0A,$3E,$FF,$32,$4C,$0F,$CD,$90 + DB $0C,$21,$00,$00,$CA,$DE,$0A,$3D,$C2,$AB,$0B,$CD,$66,$0C + DB $7D,$B4,$CA,$AB,$0B,$2B,$22,$4D,$0F,$CD,$44,$0D,$C3,$85 + DB $08,$CD,$DD,$0B,$FE,$0D,$C2,$F5,$0A,$CD,$44,$0D,$C3,$FE + DB $06,$01,$0B,$00,$21,$B3,$0D,$BE,$CA,$08,$0B,$23,$04,$0D + DB $C2,$FB,$0A,$C3,$AB,$0B,$CD,$DD,$0B,$FE,$0D,$C2,$AB,$0B + DB $C5,$CD,$15,$0C,$CD,$1A,$0D,$CD,$C5,$0B,$CD,$B6,$0B,$CD + DB $90,$0C,$B7,$CA,$FE,$06,$3D,$C2,$AB,$0B,$CD,$66,$0C,$C1 + DB $78,$FE,$05,$D2,$59,$0B,$7C,$B7,$C2,$AB,$0B,$7D,$FE,$02 + DB $D2,$AB,$0B,$CD,$E3,$0C,$67,$41,$3E,$FE,$CD,$53,$0B,$A4 + DB $41,$67,$7D,$CD,$53,$0B,$B4,$12,$C3,$FE,$06,$05,$C8,$07 + DB $C3,$53,$0B,$C2,$69,$0B,$7C,$B7,$C2,$AB,$0B,$7D,$21,$B4 + DB $0F,$77,$C3,$FE,$06,$E5,$CD,$01,$0D,$D1,$73,$23,$72,$C3 + DB $FE,$06,$E5,$D5,$C5,$3A,$5B,$00,$E6,$7F,$CA,$94,$0B,$16 + DB $00,$5F,$21,$80,$00,$19,$7E,$FE,$1A,$CA,$A6,$0B,$21,$5B + DB $00,$34,$B7,$C3,$A7,$0B,$0E,$14,$11,$5C,$00,$CD,$A2,$06 + DB $B7,$C2,$A6,$0B,$32,$5B,$00,$C3,$7F,$0B,$37,$C1,$D1,$E1 + DB $C9,$CD,$15,$0C,$3E,$3F,$CD,$C7,$0B,$C3,$FE,$06,$0E,$0A + DB $11,$65,$0F,$CD,$A2,$06,$21,$67,$0F,$22,$63,$0F,$C9,$3E + DB $20,$E5,$D5,$C5,$5F,$0E,$02,$CD,$A2,$06,$C1,$D1,$E1,$C9 + DB $FE,$7F,$C8,$FE,$61,$D8,$E6,$5F,$C9,$E5,$21,$66,$0F,$7E + DB $B7,$3E,$0D,$CA,$F4,$0B,$35,$2A,$63,$0F,$7E,$23,$22,$63 + DB $0F,$CD,$D4,$0B,$E1,$C9,$FE,$0A,$D2,$00,$0C,$C6,$30,$C3 + DB $C7,$0B,$C6,$37,$C3,$C7,$0B,$F5,$1F,$1F,$1F,$1F,$E6,$0F + DB $CD,$F6,$0B,$F1,$E6,$0F,$C3,$F6,$0B,$3E,$0D,$CD,$C7,$0B + DB $3E,$0A,$C3,$C7,$0B,$C5,$D5,$E5,$0E,$0B,$CD,$A2,$06,$E6 + DB $01,$E1,$D1,$C1,$C9,$EB,$7C,$CD,$05,$0C,$7D,$C3,$05,$0C + DB $FE,$7F,$D2,$40,$0C,$FE,$20,$D2,$C7,$0B,$3E,$2E,$C3,$C7 + DB $0B,$EB,$2A,$5F,$0F,$7D,$93,$6F,$7C,$9A,$EB,$C9,$FE,$0D + DB $C8,$FE,$2C,$C8,$FE,$20,$C9,$D6,$30,$FE,$0A,$D8,$C6,$F9 + DB $FE,$10,$D8,$C3,$AB,$0B,$EB,$5E,$23,$56,$23,$EB,$C9,$EB + DB $21,$00,$00,$CD,$59,$0C,$29,$29,$29,$29,$B5,$6F,$CD,$DD + DB $0B,$CD,$50,$0C,$C2,$71,$0C,$EB,$C9,$73,$23,$72,$23,$E5 + DB $21,$56,$0F,$34,$E1,$C9,$CD,$DD,$0B,$21,$56,$0F,$36,$00 + DB $23,$FE,$0D,$CA,$D5,$0C,$FE,$2C,$C2,$AE,$0C,$3E,$80,$32 + DB $56,$0F,$11,$00,$00,$C3,$B1,$0C,$CD,$6D,$0C,$CD,$85,$0C + DB $FE,$0D,$CA,$D5,$0C,$CD,$DD,$0B,$CD,$6D,$0C,$CD,$85,$0C + DB $FE,$0D,$CA,$D5,$0C,$CD,$DD,$0B,$CD,$6D,$0C,$CD,$85,$0C + DB $FE,$0D,$C2,$AB,$0B,$11,$56,$0F,$1A,$FE,$81,$CA,$AB,$0B + DB $13,$B7,$07,$0F,$C9,$E5,$21,$C3,$0D,$58,$16,$00,$19,$4E + DB $21,$B3,$0F,$7E,$EB,$E1,$C9,$CD,$E3,$0C,$0D,$CA,$FE,$0C + DB $1F,$C3,$F6,$0C,$E6,$01,$C9,$D6,$06,$21,$BE,$0D,$5F,$16 + DB $00,$19,$5E,$16,$FF,$21,$BB,$0F,$19,$C9,$CD,$01,$0D,$5E + DB $23,$56,$EB,$C9,$7E,$CD,$C7,$0B,$78,$FE,$05,$D2,$2B,$0D + DB $CD,$F3,$0C,$CD,$F6,$0B,$C9,$F5,$3E,$3D,$CD,$C7,$0B,$F1 + DB $C2,$3D,$0D,$21,$B4,$0F,$7E,$CD,$05,$0C,$C9,$CD,$12,$0D + DB $CD,$2E,$0C,$C9,$21,$B3,$0D,$06,$00,$CD,$15,$0C,$C5,$E5 + DB $CD,$1A,$0D,$E1,$C1,$04,$23,$78,$FE,$0B,$D2,$66,$0D,$FE + DB $05,$DA,$4C,$0D,$CD,$C5,$0B,$C3,$4C,$0D,$CD,$C5,$0B,$CD + DB $85,$0E,$F5,$D5,$C5,$CD,$93,$09,$D2,$86,$0D,$2A,$B9,$0F + DB $22,$0C,$00,$21,$10,$00,$36,$FF,$CD,$06,$00,$C3,$AF,$0D + DB $2B,$22,$5F,$0F,$2A,$B9,$0F,$7E,$CD,$05,$0C,$23,$CD,$45 + DB $0C,$DA,$AF,$0D,$F5,$CD,$C5,$0B,$F1,$B3,$CA,$AB,$0D,$5E + DB $23,$56,$EB,$CD,$2E,$0C,$C3,$AF,$0D,$7E,$CD,$05,$0C,$C1 + DB $D1,$F1,$C9,$43,$5A,$4D,$45,$49,$41,$42,$44,$48,$53,$50 + DB $F6,$F4,$FC,$FA,$FE,$01,$07,$08,$03,$05,$21,$00,$00,$22 + DB $4D,$0F,$C9,$F3,$22,$B7,$0F,$E1,$2B,$22,$B9,$0F,$F5,$21 + DB $02,$00,$39,$F1,$31,$B7,$0F,$E5,$F5,$C5,$D5,$2A,$B9,$0F + DB $7E,$FE,$FF,$F5,$E5,$21,$4F,$0F,$7E,$36,$00,$B7,$CA,$04 + DB $0E,$3D,$47,$23,$5E,$23,$56,$23,$7E,$12,$78,$C3,$F3,$0D + DB $E1,$F1,$CA,$28,$0E,$23,$22,$B9,$0F,$EB,$21,$A8,$06,$4E + DB $23,$46,$CD,$57,$08,$DA,$28,$0E,$CD,$C8,$0D,$2A,$4A,$0F + DB $EB,$3E,$82,$B7,$37,$C3,$85,$08,$FB,$2A,$4D,$0F,$7C,$B5 + DB $CA,$4E,$0E,$2B,$22,$4D,$0F,$CD,$1F,$0C,$C2,$4E,$0E,$3A + DB $4C,$0F,$B7,$C2,$48,$0E,$CD,$85,$0E,$C3,$85,$08,$CD,$44 + DB $0D,$C3,$85,$08,$CD,$C8,$0D,$3E,$2A,$CD,$C7,$0B,$2A,$B9 + DB $0F,$CD,$93,$09,$D2,$62,$0E,$22,$0C,$00,$CD,$2E,$0C,$2A + DB $B7,$0F,$22,$5D,$0F,$C3,$FE,$06,$11,$0D,$00,$21,$2F,$0F + DB $7E,$A0,$23,$BE,$23,$CA,$81,$0E,$14,$1D,$C2,$74,$0E,$5A + DB $16,$00,$C9,$2A,$B9,$0F,$46,$23,$E5,$CD,$6E,$0E,$21,$49 + DB $0F,$73,$21,$9C,$0E,$19,$19,$5E,$23,$56,$EB,$E9,$B8,$0E + DB $E0,$0E,$B8,$0E,$E0,$0E,$BE,$0E,$F2,$0E,$04,$0F,$26,$0F + DB $26,$0F,$23,$0F,$23,$0F,$19,$0F,$26,$0F,$14,$0F,$CD,$CE + DB $0E,$C2,$29,$0F,$CD,$D9,$0E,$C3,$29,$0F,$3A,$A8,$06,$BB + DB $C0,$3A,$A9,$06,$BA,$C9,$C1,$E1,$5E,$23,$56,$23,$E5,$C5 + DB $C3,$C4,$0E,$2A,$B5,$0F,$5E,$23,$56,$C9,$CD,$CE,$0E,$CA + DB $ED,$0E,$C1,$C5,$3E,$02,$C3,$2B,$0F,$D1,$D5,$C3,$29,$0F + DB $78,$FE,$FF,$C2,$FC,$0E,$AF,$C3,$2D,$0F,$E6,$38,$5F,$16 + DB $00,$C3,$29,$0F,$2A,$B7,$0F,$EB,$CD,$C4,$0E,$C2,$29,$0F + DB $C3,$BE,$0E,$C3,$29,$0F,$D1,$D5,$C3,$29,$0F,$CD,$D9,$0E + DB $C1,$C5,$3E,$02,$C3,$2B,$0F,$D1,$13,$D5,$D1,$13,$D5,$3E + DB $01,$3C,$37,$E1,$C9,$FF,$C3,$C7,$C2,$FF,$CD,$C7,$C4,$FF + DB $C9,$C7,$C7,$FF,$E9,$C7,$06,$C7,$C6,$CF,$01,$E7,$22,$C7 + DB $C0,$F7,$D3,$03,$CD,$39,$08,$CD,$2E,$08,$CD,$13,$08,$FE + DB $1A,$C2,$59,$18,$C9,$C3,$DB,$17,$C9,$01,$8F,$03,$CD,$AF + DB $09,$C9,$2A,$20,$1D,$4D,$CD,$5E,$08,$11,$9E,$03,$01,$E1 + DB $1D,$CD,$FD,$15,$32,$B6,$1D,$01,$E1,$1D,$C5,$1E,$03,$01 + DB $55,$1F,$CD,$18,$0A,$3A,$E1,$1D,$E6,$7F,$32,$E1,$1D,$3A + DB $E2,$1D,$E6,$7F,$32,$E2,$1D,$01,$A2,$03,$CD,$EA,$15,$01 + DB $D8,$1D,$CD,$B3,$08,$01,$D8,$1D,$CD,$E3,$08,$3A,$5F,$1E + DB $FE,$FF,$C2,$B3,$18,$01,$A5,$03,$CD,$AF,$09,$21,$F8,$1D + DB $36,$00,$21,$00,$00,$00,$20,$90,$00,$40,$00,$08,$21,$10 + DB $92,$10,$21,$12,$42,$48,$00,$09,$10,$02,$40,$00,$10,$40 + DB $08,$08,$41,$02,$00,$82,$42,$48,$09,$09,$20,$42,$21,$01 + DB $20,$08,$22,$12,$11,$10,$10,$88,$42,$48,$49,$24,$24,$92 + DB $42,$49,$24,$42,$49,$20,$84,$24,$84,$10,$92,$09,$10,$92 + DB $48,$49,$09,$20,$82,$44,$04,$24,$90,$90,$84,$91,$08,$48 + DB $90,$90,$84,$84,$44,$24,$24,$20,$48,$08,$04,$08,$08,$00 + DB $04,$00,$20,$84,$20,$08,$42,$10,$92,$04,$21,$24,$44,$24 + DB $04,$88,$22,$24,$92,$42,$24,$90,$92,$44,$08,$41,$08,$21 + DB $02,$10,$11,$10,$41,$08,$42,$08,$08,$90,$92,$49,$24,$84 + DB $90,$92,$12,$48,$20,$42,$01,$24,$90,$92,$49,$09,$24,$92 + DB $48,$08,$92,$49,$08,$24,$08,$91,$04,$81,$12,$48,$11,$24 + DB $89,$21,$20,$24,$92,$49,$20,$00,$00,$00,$00,$00,$00,$00 + DB $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 + DB $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 + DB $00,$00,$00,$00,$24,$92,$49,$24,$84,$01,$24,$81,$04,$92 + DB $08,$02,$10,$10,$02,$04,$92,$41,$09,$21,$08,$41,$00,$AA + DB $AA,$AA,$AA,$AA,$AA,$A0,$00,$40,$92,$24,$92,$49,$24,$89 + DB $22,$21,$24,$92,$48,$24,$49,$00,$24,$92,$49,$11,$20,$92 + DB $22,$21,$09,$24,$90,$91,$10,$02,$12,$41,$24,$88,$80,$92 + DB $09,$10,$90,$11,$02,$08,$04,$20,$00,$08,$48,$84,$48,$00 + DB $90,$04,$00,$90,$84,$41,$02,$08,$41,$22,$08,$49,$20,$40 + DB $00,$00,$08,$04,$42,$42,$08,$91,$09,$20,$00,$84,$00,$24 + DB $90,$84,$11,$10,$10,$92,$41,$22,$24,$12,$00,$90,$00,$24 + DB $24,$49,$24,$92,$00,$08,$92,$24,$12,$48,$21,$11,$21,$02 + DB $21,$20,$89,$08,$92,$42,$48,$22,$09,$21,$12,$49,$11,$20 + DB $42,$09,$02,$04,$20,$90,$88,$88,$10,$02,$00,$10,$10,$11 + DB $04,$04,$24,$24,$90,$01,$00,$00,$81,$10,$90,$21,$08,$02 + DB $08,$42,$02,$00,$44,$21,$08,$80,$00,$00,$04,$00,$10,$09 + DB $20,$08,$24,$04,$21,$04,$90,$92,$42,$49,$09,$04,$02,$02 + DB $04,$44,$04,$01,$08,$04,$12,$40,$89,$11,$22,$10,$80,$21 + DB $24,$90,$49,$24,$24,$48,$89,$10,$81,$22,$00,$00,$00,$04 + DB $21,$00,$81,$01,$02,$00,$10,$88,$82,$49,$01,$10,$89,$24 + DB $49,$24,$84,$92,$49,$24,$10,$10,$81,$04,$88,$05,$55,$55 + DB $55,$24,$92,$10,$00,$90,$24,$08,$41,$10,$12,$24,$90,$90 + DB $20,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 + DB $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 + DB $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 + DB $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 + DB $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 + DB $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 + DB $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$02,$00 + DB $00,$02,$00,$00,$02,$00,$00,$02,$00,$00,$02,$00,$00,$02 + DB $00,$00,$02,$00,$00,$02,$00,$00,$02,$00,$00,$22,$48,$40 + DB $02,$00,$00,$02,$00,$00,$02,$00,$00,$02,$00,$00,$02,$00 + DB $00,$02,$00,$00,$02,$00,$00,$02,$00,$00,$02,$00,$00,$02 + DB $00,$00,$02,$00,$00,$02,$00,$00,$02,$00,$00,$02,$00,$00 + DB $02,$00,$00,$22,$48,$42,$02,$00,$00,$02,$00,$00,$02,$00 + DB $00,$02,$00,$00,$02,$00,$00,$02,$00,$00,$02,$00,$00,$08 + DB $48,$69,$08,$6F,$48,$0E,$48,$00,$02,$00,$00,$02,$00,$00 + DB $02,$00,$00,$02,$00,$00,$02,$00,$00,$22,$48,$48,$08,$48 + DB $6F,$12,$48,$00,$02,$00,$00,$02,$00,$00,$02,$00,$00,$02 + DB $00,$00,$02,$00,$00,$02,$00,$00,$02,$00,$00,$02,$00,$00 + DB $0F,$6B,$00,$13,$6B,$00,$08,$6D,$68,$02,$00,$00,$02,$00 + DB $00,$22,$48,$46,$02,$00,$00,$02,$00,$00,$02,$00,$00,$02 + DB $00,$00,$02,$00,$00,$02,$00,$00,$AA,$20,$60,$A6,$60,$20 + DB $2E,$44,$40,$08,$6F,$40,$8E,$00,$00,$92,$00,$00,$D2,$30 + DB $00,$0A,$39,$27,$AA,$21,$60,$A6,$60,$21,$26,$44,$40,$08 + DB $40,$6F,$02,$00,$00,$96,$00,$00,$02,$00,$00,$0A,$3A,$27 + DB $AA,$22,$60,$A6,$60,$22,$2E,$44,$42,$08,$6F,$42,$02,$00 + DB $00,$02,$00,$00,$D2,$31,$00,$0A,$27,$39,$AA,$23,$60,$A6 + DB $60,$23,$26,$44,$42,$08,$42,$6F,$02,$00,$00,$02,$00,$00 + DB $D2,$32,$00,$0A,$27,$3A,$AA,$24,$60,$A6,$60,$24,$2E,$44 + DB $44,$02,$00,$00,$02,$00,$00,$02,$00,$00,$02,$00,$00,$9A + DB $00,$00,$AA,$25,$60,$A6,$60,$25,$26,$44,$44,$02,$00,$00 + DB $02,$00,$00,$02,$00,$00,$02,$00,$00,$9E,$00,$00,$AA,$26 + DB $60,$A6,$60,$26,$2E,$44,$46,$08,$6F,$46,$02,$00,$00,$02 + DB $00,$00,$02,$00,$00,$02,$00,$00,$AA,$27,$60,$A6,$60,$27 + DB $26,$44,$46,$08,$46,$6F,$02,$00,$00,$02,$00,$00,$02,$00 + DB $00,$02,$00,$00,$02,$00,$00,$02,$00,$00,$02,$00,$00,$02 + DB $00,$00,$02,$00,$00,$02,$00,$00,$02,$00,$00,$02,$00,$00 + DB $02,$00,$00,$02,$00,$00,$02,$00,$00,$02,$00,$00,$02,$00 + DB $00,$02,$00,$00,$02,$00,$00,$02,$00,$00,$02,$00,$00,$02 + DB $00,$00,$02,$00,$00,$02,$00,$00,$02,$00,$00,$02,$00,$00 + DB $02,$00,$00,$02,$00,$00,$02,$00,$00,$02,$00,$00,$02,$00 + DB $00,$02,$00,$00,$02,$00,$00,$02,$00,$00,$02,$00,$00,$02 + DB $00,$00,$E0,$00,$00,$E1,$00,$00,$E2,$00,$00,$E3,$00,$00 + DB $02,$00,$00,$02,$00,$00,$02,$00,$00,$02,$00,$00,$E4,$00 + DB $00,$E5,$00,$00,$E6,$00,$C3,$00,$00,$C3,$00,$00,$C3,$00 + DB $00,$C3,$00,$00,$C3,$00,$00,$C3,$00,$00,$C3,$00,$00,$C3 + DB $00,$00,$C3,$00,$00,$C3,$00,$00,$C3,$00,$00 + + ;; The following code will be relocated to $b821 +relocated_area: ;[1621] + REPT 640 + nop + ENDR + + ;[18a1] d0 + DB $D0,$5C,$00,$D1,$7E,$00,$D2,$7B,$00,$D3,$7D,$00,$D4,$40 + DB $00,$D5,$7C,$00,$D6,$7E,$61,$D7,$7E,$65,$D8,$7E,$69,$D9 + DB $7E,$6F,$DA,$7E,$75,$DB,$5E,$61,$DC,$5E,$65,$DD,$5E,$69 + DB $DE,$5E,$6F,$DF,$5E,$75,$E0,$5B,$00,$E1,$5C,$00,$E2,$5D + DB $00,$00,$61,$DB,$D6,$65,$DC,$D7,$69,$DD,$D8,$6F,$DE,$D9 + DB $75,$DF,$DA,$00,$00,$79,$FE,$F1,$28,$08,$FE,$F2,$20,$0B + DB $3E,$02,$18,$02,$3E,$01,$32,$EB,$BA,$37,$C9,$3A,$EB,$BA + DB $B7,$28,$12,$21,$DB,$BA,$06,$05,$7E,$B9,$28,$0A,$23,$23 + DB $23,$10,$F7,$AF,$32,$EB,$BA,$C9,$3A,$EB,$BA,$06,$00,$4F + DB $09,$4E,$18,$F1,$C3,$26,$BB,$79,$CB,$7F,$C8,$21,$A1,$BA + DB $7E,$B7,$C8,$B9,$23,$28,$04,$23,$23,$18,$F5,$4E,$23,$22 + DB $5B,$BB,$7E,$B7,$C8,$79,$11,$46,$BB,$18,$0F,$3E,$08,$11 + DB $4D,$BB,$18,$08,$2A,$5B,$BB,$4E,$AF,$11,$26,$BB,$ED,$53 + DB $24,$BB,$B7,$C9,$00,$00,$C9 + ;[195e] + REPT 1164 + nop + ENDR + + DB "TT" ;[1dea] + DB 00 + DB 00 + DB 00 + DB 00 + DB 00 + DB 00 + ;[1df2] + DB $21,$b8,$00 + DB $00 + DB $ec,$ba,$23 + DB $bb + DB $5d + DB $bb + ;; - - - - Relocated code ends here - - - - + + DB "8.10" ;[1dfc] + +kbdlayout: ;[1e00] + DB $00,$1b,$24,$2a,$d2,$22,$27,$28,$29,$d3,$5f,$d0,$d4,$21,$3f,$7f + DB $5c,$03,$09,$61,$7a,$65,$72,$74,$79,$75,$69,$6f,$70,$26,$f1,$5d + DB $71,$73,$64,$66,$67,$68,$6a,$6b,$6c,$6d,$d5,$0d,$5b,$18,$77,$78 + DB $63,$76,$62,$6e,$2c,$3b,$3a,$3d,$0a,$20,$0b,$0a,$08,$0c,$37,$38 + DB $39,$15,$34,$35,$36,$2d,$31,$32,$33,$2e,$30,$00,$0d,$00,$80,$81 + DB $82,$83,$84,$85,$86,$87,$88,$89,$8a,$8b,$8c,$8d,$8e,$00,$00,$00 + DB $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 + DB $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 + DB $00,$1b,$3c,$31,$32,$33,$34,$35,$36,$37,$38,$39,$30,$23,$40,$7f + DB $5e,$03,$09,$41,$5a,$45,$52,$54,$59,$55,$49,$4f,$50,$3e,$f2,$7d + DB $51,$53,$44,$46,$47,$48,$4a,$4b,$4c,$4d,$25,$0d,$7b,$18,$57,$58 + DB $43,$56,$42,$4e,$2f,$2e,$2d,$2b,$0a,$20,$0b,$0a,$08,$0c,$37,$38 + DB $39,$05,$34,$35,$36,$2d,$31,$32,$33,$2e,$30,$00,$0d,$00,$80,$81 + DB $82,$83,$84,$85,$86,$87,$88,$89,$8a,$8b,$8c,$8d,$8e,$00,$00,$00 + DB $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 + DB $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 + DB $00,$1b,$00,$00,$00,$00,$00,$00,$00,$00,$1f,$00,$00,$00,$00,$7f + DB $1c,$03,$09,$01,$1a,$05,$12,$14,$19,$15,$09,$0f,$10,$00,$1e,$1d + DB $11,$13,$04,$06,$07,$08,$0a,$0b,$0c,$0d,$00,$0d,$1b,$18,$17,$18 + DB $03,$16,$02,$0e,$00,$00,$00,$00,$0a,$20,$0b,$0a,$08,$0c,$37,$38 + DB $39,$15,$34,$35,$36,$2d,$31,$32,$33,$2e,$30,$00,$0d,$00,$80,$81 + DB $82,$83,$84,$85,$86,$87,$88,$89,$8a,$8b,$8c,$8d,$8e,$00,$00,$00 + DB $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 + DB $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 + + + ; This is a piece of a system ROM copied from $c180 to + ; $1f80. It is not exactly the same ROM we have in our Sanco + ; 8003, but the code structure matches. + ; Here, is just filled with DB since it has no meaning in + ; this code. +romjunk0: + DB $EB,$CD,$49,$C4,$18,$F5,$E3,$C9,$C5,$D5,$E5,$32,$B8,$FF + DB $3E,$0A,$32,$BF,$FF,$ED,$43,$B9,$FF,$ED,$53,$BB,$FF,$22 + DB $BD,$FF,$CD,$0E,$C4,$3A,$BA,$FF,$E6,$F0,$CA,$D1,$C1,$FE + DB $40,$CA,$C7,$C1,$FE,$80,$CA,$C2,$C1,$FE,$20,$CA,$CC,$C1 + DB $FE,$F0,$CA,$D6,$C1,$3E,$FF,$C3,$DB,$C1,$CD,$DF,$C1,$18 + DB $14,$CD,$35,$C2,$18,$0F,$CD,$94,$C3,$18,$0A,$CD,$7C,$C3 + DB $18,$05,$CD,$CE,$C2,$18,$00,$E1,$D1,$C1,$C9,$CD,$94,$C3 + DB $CD,$A2,$C2,$D5,$CD,$07,$C4,$0E,$C5,$3A,$B8,$FF,$B7,$20 + DB $02,$CB,$B1,$CD,$00,$C4,$F3,$CD,$39,$C3,$D1,$0E,$C1,$43 + DB $2A,$BD,$FF,$DB,$82,$CB,$57,$28,$FA,$DB,$C0,$CB,$6F,$28 + DB $07,$ED,$A3,$20,$F0,$15,$20,$ED,$D3,$DC,$FB,$CD,$DF,$C3 + DB $3A,$C0,$FF,$E6,$C0,$FE,$40,$20,$10,$CD,$8B,$C2,$3A,$BF + DB $FF,$3D,$32,$BF,$FF,$C2,$E2,$C1,$3E,$FF,$C9,$AF,$C9,$CD + DB $94,$C3,$CD,$A2,$C2,$D5,$CD,$07,$C4,$0E,$C6,$3A,$B8,$FF + DB $B7,$20,$02,$CB,$B1,$CD,$00,$C4,$F3,$CD,$39,$C3,$D1,$0E + DB $C1,$43,$2A,$BD,$FF,$DB,$82,$CB,$57,$28,$FA,$DB,$C0,$CB + DB $6F,$28,$07,$ED,$A2,$20,$F0,$15,$20,$ED,$D3,$DC,$FB,$CD + DB $DF,$C3,$3A,$C0,$FF,$E6,$C0,$FE,$40,$20,$10,$CD,$8B,$C2 + DB $3A,$BF,$FF,$3D + +shortcuts_str: ;[2080] + DB " F1:DIR \x7F F2:STAT \x7F F3:MBASIC\x0D\x7F F4:PIP \x7F F5:TYPE \x7F " + + ;; The system ROM code starts back here, same as before +romjunk1: + DB $3F,$FE,$FF,$C8,$06,$14,$3A,$B8,$FF,$FE,$03,$28,$02,$06 + DB $40,$C5,$3A,$31,$2E,$31,$31,$39,$00,$C4,$ED,$4B,$B9,$FF + DB $CD,$00,$C4,$3A,$B8,$FF,$4F,$CD,$00,$C4,$0E,$05,$3A,$B8 + DB $FF,$FE,$03,$28,$02,$0E + +entrypoint: + di ;[2100] Beginning of the critical section + im 2 ;[2101] + ld a,$ff ;[2103] + ld i,a ;[2105] + ld sp,$0151 ;[2107] Setup stack pointer + ld a,$01 ;[210a] Configure the CRTC Horizontal disp character + out ($a0),a ;[210c] register (address 1) + ld a,$00 ;[210e] to 0 + out ($a1),a ;[2110] (no displayed character?) + ld a,$00 ;[2112] + ld ($0003),a ;[2114] I/O byte: set to zero (see below) + call loadrelocated ;[2117] load some pointers and routines used by ROM and CP/M bios + call loadkeys ;[211a] load the keyboard layout in the proper area + call loadshortcuts ;[211d] load the content of the shortcuts bar + ld a,(iobyte_init) ;[2120] Initialize I/O byte + ld ($0003),a ;[2123] I/O byte [https://www.mark-ogden.uk/mirrors/www.cirsovius.de/CPM/Projekte/Artikel/Grundlagen/IOByte/IOByte-de.html] + ld c,$09 ;[2126] C_WRITESTR(welcome_str) + ld de,welcome_str ;[2128] the welcome string with CP/M version + call SYSCALL ;[212b] + ld a,$01 ;[212e] Configure the CRTC Horizontal disp character + out ($a0),a ;[2130] register (address 1) + ld a,$50 ;[2132] to 80 + out ($a1),a ;[2134] (number of columns) + call initialize ;[2136] Initialize all the other peripherals + in a,($81) ;[2139] PORTB |= 0x02 + set 1,a ;[213b] which is: mute parity error + out ($81),a ;[213d] + ei ;[213f] End of the critical section + ld c,$00 ;[2140] P_TERMCPM - System reset + call SYSCALL ;[2142] + + ; Load the relocated area in place (see SLF80037.relocated.asm) + ; This area contains pointers to keyboard layout area, keyboard shortcuts, + ; the dead keys handling, and the custom escape routine used in ROM's putchar. +loadrelocated: + call bank_off ;[2145] + ld de,$b821 ;[2148] base address of where the area should be relocated + ld hl,relocated_area ;[214b] + ld bc,$07db ;[214e] relocated area size + ldir ;[2151] memcpy($b821, relocated_area, 0x07db) + call bank_on ;[2153] + ret ;[2156] + +loadkeys: + call bank_off ;[2157] + ld hl,($bff2) ;[215a] load the pointer to the designated keymap area + ld de,kbdlayout ;[215d] load pointer to keymap table + ex de,hl ;[2160] + ld bc,$0280 ;[2161] keymap table length + ldir ;[2164] memcpy(*$bff2, kbdlayout, 0x0280) + call bank_on ;[2166] + ret ;[2169] + + ; This routine loads the content of the 25th line on the + ; screen, which containts the shortcuts (F1, F2, ...) +loadshortcuts: + call bank_off ;[216a] + ld hl,($ffad) ;[216d] *$ffad=$feb4, loaded by cp/m bios + ld ($bff4),hl ;[2170] update the pointer to the shortcuts (used by keyboard routine + ld de,shortcuts_str ;[2173] in CP/M bios to recall commands) + ex de,hl ;[2176] + ld bc,$0050 ;[2177] length of shortcuts string + ldir ;[217a] memcpy(*$ffad, shortcuts_str, 0x0050) + call bank_on ;[217c] + ret ;[217f] + + ; This initialization is more or less the same as V1.01 ROM +initialize: + call kbd_sio_init ;[2180] initialize the SIO keyboard channel + call comm_sio_init ;[2183] initialize the SIO communication channel + call timer3_init ;[2186] initialize the timer channel 3 + call timer12_init ;[2189] initialize the timer channels 1 and 2 + ret ;[218c] + + ; Configure the SIO channel 2, dedicated to the keyboard + ; communication +kbd_sio_init: + ld hl,kbd_sio_cfg ;[218d] +L2190: + ld a,(hl) ;[2190] + inc hl ;[2191] + cp $ff ;[2192] + jr z,L219a ;[2194] Repeat configuration loop until *hl != 0xff + out ($b3),a ;[2196] + jr L2190 ;[2198] +L219a: + in a,($b2) ;[219a] Flush SIO buffer + in a,($b2) ;[219c] + in a,($b2) ;[219e] + ret ;[21a0] + +kbd_sio_cfg: ;[21a1] + DB $00 ; Register 0 + DB $18 ; Channel reset + DB $1b ; Channel reset? but with another register pointer (which is forbidden by the manual) + DB $00 ; Register 0 + DB $10 ; Reset interrupts + DB $00 ; Register 0 + DB $10 ; Reset interrupts + DB $04 ; Register 4 + DB $4c ; 2 stop bits + x16 clock mode + DB $01 ; Register 1 + DB $18 ; interrupts enabled on all received character + DB $03 ; Register 3 + DB $c1 ; 8 bit per character (Rx) + Rx enable + DB $05 ; Register 5 + DB $6a ; RTS + Tx enable + 8 bit per character (Tx) + DB $02 ; Register 2 + DB $88 ; Vector interrupt address (TODO) + DB $ff + +timer3_cfg: ;[21b3] + DB $e0,$80 ; timer x: vector address ($80), common for all channels + DB $e3,$03 ; timer 3: software reset + DB $e3,$05 ; timer 3: time constant follows + DB $e3,$01 ; timer 3: time constant = 1 + DB $e3,$c1 ; timer 3: enable interrupts + counter mode + DB $ff + +timer3_init: + ld hl,timer3_cfg ;[21be] +L21c1: + ld a,(hl) ;[21c1] load IO address + inc hl ;[21c2] + cp $ff ;[21c3] loop until *hl != 0xff + ret z ;[21c5] + ld c,a ;[21c6] put IO address in c + ld a,(hl) ;[21c7] load register value + inc hl ;[21c8] + out (c),a ;[21c9] + jp L21c1 ;[21cb] + + ; Configure the SIO channel 1, dedicated to communication + ; with other devices +comm_sio_init: + ld a,$18 ;[21ce] Channel reset + out ($b1),a ;[21d0] + ld c,$b1 ;[21d2] address of SIO channel 1 + ld hl,comm_sio_cfg ;[21d4] + ld b,$0e ;[21d7] sizeof(comm_sio_cfg) + otir ;[21d9] repeat io(c) = (*hl++) over all table + ret ;[21db] + + ; Configure timers 1 and 2 +timer12_init: + ld b,$03 ;[21dc] load number of register to program + ld c,$e1 ;[21de] load peripheral IO address + ld hl,timer12_cfg ;[21e0] load configuration table +L21e3: + ld a,(hl) ;[21e3] load register value + out (c),a ;[21e4] and write it to CTC + inc hl ;[21e6] + dec b ;[21e7] + jr nz,L21e3 ;[21e8] repeat for all three register + ld a,(hl) ;[21ea] check if table end was reached + inc c ;[21eb] move IO pointer to the other channel + or a ;[21ec] + ld b,$03 ;[21ed] + jr nz,L21e3 ;[21ef] repeat until table value != 0 + ret ;[21f1] + + ; Enable bank switching (access to whole 64k DRAM) +bank_on: + in a,($81) ;[21f2] + set 0,a ;[21f4] + out ($81),a ;[21f6] + ret ;[21f8] + + ; Disable bank switching +bank_off: + in a,($81) ;[21f9] + res 0,a ;[21fb] + out ($81),a ;[21fd] + ret ;[21ff] + + ;; The system ROM code starts back here, same as before +romjunk2: + DB $CD,$EE,$C3,$79,$D3,$C1,$C9,$DB,$C0,$CB,$67,$20,$FA,$C9 + DB $06,$01,$79,$E6,$03,$B7,$28,$05,$CB,$00,$3D,$20,$FB,$3A + DB $C7,$FF,$4F,$A0,$C0,$79,$B0,$32,$C7,$FF,$CD,$7C,$C3,$C9 + DB $C5,$E5,$21,$47,$C4,$CD,$07,$C4,$0E,$03,$CD,$00,$C4,$4E + DB $23,$CD,$00,$C4,$4E,$CD,$00,$C4,$AF,$32,$C7,$FF,$E1,$C1 + DB $C9,$6F,$1B,$F5,$C5,$D5,$E5,$DD,$E5,$FD,$E5,$CD,$80,$C6 + DB $3A,$D8,$FF,$B7,$C2,$C9,$C9,$3A,$CC,$FF,$FE,$FF,$CA,$89 + DB $C6,$B7,$C2,$A4,$C4,$79,$FE,$1B,$28,$30,$FE,$20,$D2,$A4 + DB $C4,$FE,$0D,$CA,$0A,$C5,$FE,$0A,$CA,$18,$C5,$FE,$0B,$CA + DB $3E,$C5,$FE,$0C,$CA,$55,$C5,$FE,$08,$CA,$81,$C5,$FE,$1E + DB $CA,$C1,$C5,$FE,$1A,$CA,$D4,$C5,$FE,$07,$CC,$DA,$C5,$C3 + DB $89,$C6,$3E,$01,$32,$D8,$FF,$C3,$89,$C6,$FD,$E5,$E1,$CD + DB $FB,$C6,$71,$CD,$7B,$C7,$3A,$D1,$FF,$47,$3A,$D2,$FF,$A6 + DB $B0,$77,$CD,$84,$C7,$CD,$DE,$C5,$38,$06,$CD,$F9,$C5,$C3 + DB $89,$C6,$3A,$CB,$FF,$47,$3A,$CD,$FF,$B8,$28,$17,$04,$78 + DB $32,$CB,$FF,$3A,$C9,$FF,$B7,$20,$06,$CD,$F9,$C5,$C3,$89 + DB $C6,$CD,$06,$C6,$C3,$89,$C6,$3A,$C9,$FF,$B7,$20,$09,$CD + DB $F9,$C5,$CD,$14,$C6,$C3,$89,$C6,$3A,$CD,$FF,$47,$3A,$D0 + DB $FF,$4F,$CD,$D7,$C6,$CD,$02,$C7,$CD,$14,$C6,$C3,$89,$C6 + DB $3A,$D0,$FF,$32,$CA,$FF,$4F,$3A,$CB,$FF,$47,$C3,$CB,$C5 + DB $3A,$CB,$FF,$47,$3A,$CD,$FF,$B8,$28,$0F,$04,$78,$32,$CB + DB $FF,$FD,$E5,$E1,$11,$50,$00,$19,$C3,$CE,$C5,$CD,$14,$C6 + DB $3A,$CB,$FF,$47,$3A,$CA,$FF,$4F,$18,$E9,$3A,$CB,$FF,$47 + DB $3A,$CE,$FF,$B8,$CA,$89,$C6,$05,$78,$32,$CB,$FF,$3A,$CA + DB $FF,$4F,$C3,$CB,$C5,$CD,$DE,$C5,$3A,$CB,$FF,$47,$38,$03 + DB $C3,$CB,$C5,$3A,$D0,$FF,$32,$CA,$FF,$4F,$3A,$CB,$FF,$47 + DB $3A,$CD,$FF,$B8,$28,$08,$04,$78,$32,$CB,$FF,$C3,$CB,$C5 + DB $C5,$CD,$14,$C6,$C1,$18,$4A,$3A,$CA,$FF,$4F,$3A,$D0,$FF + DB $B9,$28,$13,$0D,$3A,$D1,$FF,$CB,$5F,$28,$01,$0D,$79,$32 + DB $CA,$FF,$3A,$CB,$FF,$47,$18,$2D,$3A,$CF,$FF,$47,$3A,$D1 + DB $FF,$CB,$5F,$28,$01,$05,$78,$32,$CA,$FF,$4F,$3A,$CB,$FF + DB $47,$3A,$CE,$FF,$B8,$CA,$89,$C6,$05,$78,$32,$CB,$FF,$18 + DB $0A,$AF,$32,$CB,$FF,$32,$CA,$FF,$01,$00,$00,$CD,$D7,$C6 + DB $CD,$02,$C7,$C3,$89,$C6,$CD,$4A,$C7,$C3,$89,$C6,$AF,$D3 + DB $DA,$C9,$3A,$CA,$FF,$4F,$0C,$3A,$D1,$FF,$CB,$5F,$28,$01 + DB $0C,$3A,$CF,$FF,$B9,$79,$30,$03,$3A,$D0,$FF,$32,$CA,$FF + DB $C9,$23,$3A,$D1,$FF,$CB,$5F,$28,$01,$23,$CD,$02,$C7,$C9 + DB $3A,$C8,$FF,$5F,$16,$00,$FD,$E5,$E1,$19,$CD,$02,$C7,$C9 + DB $3A,$C9,$FF,$B7,$20,$13,$DD,$E5,$E1,$11,$50,$00,$19,$CD + DB $28,$C7,$06,$17,$CD,$E0,$C7,$CD,$56,$C6,$C9,$3A,$D0,$FF + DB $4F,$3A,$CE,$FF,$47,$3A,$CE,$FF,$57,$3A,$CD,$FF,$92,$28 + DB $0B,$57,$04,$CD,$D7,$C6,$CD,$8D,$C7,$15,$20,$F6,$3A,$CD + DB $FF,$57,$3A,$CF,$FF,$5F,$CD,$EB,$C7,$C9,$DD,$E5,$E1,$11 + DB $30,$07,$06,$50,$19,$11,$00,$20,$CD,$7B,$C7,$CD,$FB,$C6 + DB $E5,$C5,$1E,$00,$CD,$76,$C6,$C1,$E1,$CD,$84,$C7,$1E,$20 + DB $73,$23,$CB,$5C,$CC,$FB,$C6,$10,$F7,$C9,$DD,$2A,$D4,$FF + DB $FD,$2A,$D6,$FF,$C9,$CD,$CE,$C6,$FD,$E1,$DD,$E1,$E1,$D1 + DB $C1,$F1,$C9,$21,$C9,$FF,$AF,$77,$23,$77,$23,$77,$23,$77 + DB $23,$36,$17,$23,$77,$23,$36,$4F,$23,$77,$23,$77,$23,$36 + DB $80,$23,$3A,$55,$C8,$57,$DB,$D6,$CB,$6F,$28,$02,$16,$03 + DB $CB,$77,$28,$04,$CB,$EA,$CB,$F2,$72,$AF,$23,$06,$15,$77 + DB $23,$10,$FC,$C9,$DD,$22,$D4,$FF,$FD,$22,$D6,$FF,$C9,$F5 + DB $C5,$D5,$DD,$E5,$E1,$11,$50,$00,$78,$06,$05,$1F,$30,$01 + DB $19,$B7,$CB,$13,$CB,$12,$05,$20,$F4,$16,$00,$59,$19,$7C + DB $E6,$0F,$67,$D1,$C1,$F1,$C9,$7C,$E6,$07,$F6,$D0,$67,$C9 + DB $7C,$E6,$07,$67,$DD,$E5,$D1,$EB,$B7,$ED,$52,$38,$07,$28 + DB $05,$21,$00,$08,$19,$EB,$3E,$0E,$D3,$A0,$7A,$D3,$A1,$3E + DB $0F,$D3,$A0,$7B,$D3,$A1,$D5,$FD,$E1,$C9,$7C,$E6,$07,$67 + DB $CD,$41,$C7,$3E,$0C,$D3,$A0,$7C,$D3,$A1,$3E,$0D,$D3,$A0 + DB $7D,$D3,$A1,$E5,$DD,$E1,$C9,$DB,$A0,$DB,$82,$CB,$4F,$28 + DB $FA,$C9,$01,$80,$07,$DD,$E5,$E1,$11,$00,$20,$CD,$FB,$C6 + DB $72,$DB,$81,$CB,$FF,$D3,$81,$73,$CB,$BF,$D3,$81,$23,$CB + DB $5C,$CC,$FB,$C6,$0B,$78,$B1,$20,$E9,$DD,$E5,$E1,$CD,$02 + DB $C7,$AF,$32,$CA,$FF,$32,$CB,$FF,$C9,$F5,$DB,$81,$CB,$FF + DB $D3,$81,$F1,$C9,$F5,$DB,$81,$CB,$BF,$D3,$81,$F1,$C9,$D5 + DB $C5,$3E,$50,$2F,$16,$FF,$5F,$13,$CD,$9C,$C7,$C1,$D1,$C9 + DB $3A,$D0,$FF,$4F,$CD,$D7,$C6,$E5,$19,$EB,$E1,$3A,$D0,$FF + DB $47,$3A,$CF,$FF,$90,$3C,$47,$CD,$FB,$C6,$EB,$CD,$FB,$C6 + DB $EB,$C5,$D5,$E5,$0E,$02,$7E,$12,$13,$7A,$E6,$07,$F6,$D0 + DB $57,$23,$CB,$5C,$CC,$FB,$C6,$10,$EF,$0D,$28,$0A,$79,$E1 + DB $D1,$C1,$4F,$CD,$7B,$C7,$18,$E2,$CD,$84,$C7,$C9,$D5,$C5 + DB $11,$50,$00,$CD,$9C,$C7,$C1,$D1,$C9,$7B,$91,$3C,$5F,$7A + DB $90,$3C,$57,$CD,$D7,$C6,$CD,$FB,$C6,$36,$20,$CD,$7B,$C7 + DB $36,$00,$CD,$84,$C7,$23,$1D,$20,$EF,$04,$3A,$D0,$FF,$4F + DB $3A,$CF,$FF,$91,$3C,$5F,$15,$20,$DE,$C9,$3A,$CD,$FF,$47 + DB $3A,$CE,$FF,$B8,$28,$0B,$57,$78,$92,$57,$05,$CD,$E0,$C7 + DB $15,$20,$F9,$3A,$CE,$FF,$47,$57,$3A,$D0,$FF,$4F,$3A,$CF + DB $FF,$5F,$CD,$EB,$C7,$C9,$C5,$06,$1E,$0E,$0F,$0D,$C2,$41 + DB $C8,$05,$C2,$3F,$C8,$C1,$C9,$63,$50,$54,$AA,$19,$06,$19 + DB $19,$00,$0D,$0D,$0D,$00,$00,$00,$00,$3A,$D1,$FF,$CB,$DF + DB $32,$D1,$FF,$3A,$CA,$FF,$4F,$1F,$30,$07,$FD,$23,$0C,$79 + DB $32,$CA,$FF,$AF,$C9,$21,$4B,$C8,$06,$10,$0E,$A1,$AF,$D3 + DB $A0,$3C,$ED,$A3,$20,$F9,$DD,$21,$00,$00,$CD,$4A,$C7,$CD + DB $9C,$C8,$21,$00,$00,$CD,$02,$C7,$3A,$D1,$FF,$CB,$9F,$32 + DB $D1,$FF,$AF,$C9,$3E,$06,$D3,$A0,$3E,$18,$D3,$A1,$C9,$AF + DB $32,$CE,$FF,$32,$D0,$FF,$32,$C9,$FF,$3E,$17,$32,$CD,$FF + DB $C9,$06,$04,$CD,$41,$C7,$AF,$0E,$A1,$D3,$A0,$3C,$ED,$A3 + DB $20,$F9,$C9,$3A,$D9,$FF,$B7,$20,$05,$3C,$32,$D9,$FF,$C9 + DB $79,$E6,$0F,$07,$07,$07,$07,$2F,$47,$3A,$D1,$FF,$A0,$32 + DB $D1,$FF,$AF,$C9,$AF,$32,$D1,$FF,$C9,$3A,$D9,$FF,$47,$16 + DB $00,$5F,$21,$D9,$FF,$19,$79,$D6,$20,$77,$78,$3C,$32,$D9 + DB $FF,$C9,$78,$D3,$A0,$79,$D3,$A1,$C9,$CD,$D7,$C6,$E5,$42 + DB $4B,$CD,$D7,$C6,$D1,$D5,$B7,$ED,$52,$23,$EB,$E1,$47,$CD + DB $7B,$C7,$CD,$FB,$C6,$7E,$B0,$77,$23,$1B,$7A,$B3,$20,$F4 + DB $CD,$84,$C7,$C9,$79,$FE,$44,$20,$04,$0E,$40,$18,$39,$FE + DB $45,$20,$04,$0E,$60,$18,$31,$FE,$46,$20,$04,$0E,$20,$18 + DB $29,$3A,$55,$C8,$57,$DB,$D6,$CB,$6F,$28,$02,$16,$03,$CB + DB $77,$28,$04,$CB,$EA,$CB,$F2,$7A,$32,$D3,$FF,$06,$0A,$4F + DB $CD,$FC,$C8,$3A,$56,$C8,$4F,$06,$0B,$CD,$FC,$C8,$AF,$C9 + DB $3A,$D3,$FF,$E6,$9F,$B1,$32,$D3,$FF,$4F,$06,$0A,$CD,$FC + DB $C8,$AF,$C9,$21,$D1,$FF,$CB,$C6,$AF,$C9,$21,$D1,$FF,$CB + DB $86,$AF,$C9,$21,$D1,$FF,$CB,$D6,$AF,$C9,$21,$D1,$FF,$CB + DB $96,$AF,$C9,$21,$D1,$FF,$CB,$CE,$AF,$C9,$21,$D1,$FF,$CB + DB $8E,$AF,$C9,$3A,$D1,$FF,$E6,$8F,$F6,$10,$32,$D1,$FF,$AF + DB $C9,$3A,$D1,$FF,$E6,$8F,$F6,$00,$32,$D1,$FF,$AF,$C9,$3A + DB $D1,$FF,$E6,$8F,$F6,$20,$32,$D1,$FF,$AF,$C9,$CD,$E7,$C9 + DB $FE,$01,$20,$01,$79,$32,$D8,$FF,$FE,$60,$D2,$56,$CA,$D6 + DB $31,$DA,$56,$CA,$CD,$EB,$C9,$B7,$28,$72,$C3,$89,$C6,$2A + DB $FA,$BF,$E9,$87,$21,$F8,$C9,$16,$00,$5F,$19,$5E,$23,$56 + DB $EB,$E9,$1E,$CD,$22,$CD,$60,$CA,$60,$CA,$60,$CA,$62,$CA + DB $A5,$C9,$5B,$CC,$60,$CA,$BA,$CA,$D8,$CA,$02,$CB,$50,$CB + DB $7B,$CB,$97,$CB,$62,$CA,$5B,$C8,$92,$C8,$60,$CA,$28,$C9 + DB $28,$C9,$28,$C9,$28,$C9,$7B,$C9,$82,$C9,$89,$C9,$90,$C9 + DB $97,$C9,$9E,$C9,$21,$CC,$60,$CA,$87,$CC,$BB,$CC,$60,$CA + DB $B6,$CB,$E0,$CB,$F7,$CB,$0F,$CC,$A5,$C9,$B1,$C9,$BD,$C9 + DB $A5,$C9,$5B,$CC,$C5,$C8,$E2,$C8,$71,$CC,$03,$CD,$AF,$32 + DB $D8,$FF,$32,$D9,$FF,$C3,$89,$C6,$AF,$C9,$CD,$B3,$CD,$FE + DB $01,$20,$09,$79,$FE,$31,$38,$28,$FE,$36,$30,$24,$CD,$9B + DB $CA,$B7,$C0,$3A,$DA,$FF,$E6,$0F,$3D,$87,$47,$87,$4F,$87 + DB $80,$81,$C6,$04,$2A,$F4,$BF,$16,$00,$5F,$19,$EB,$21,$DB + DB $FF,$01,$09,$00,$ED,$B0,$CD,$2D,$CD,$AF,$C9,$CD,$E7,$C8 + DB $71,$FE,$0A,$C0,$21,$DB,$FF,$06,$08,$7E,$23,$FE,$7F,$28 + DB $06,$10,$F8,$36,$7F,$18,$05,$21,$E3,$FF,$36,$20,$AF,$C9 + DB $CD,$B3,$CD,$FE,$04,$28,$04,$CD,$E7,$C8,$C9,$79,$D6,$20 + DB $5F,$21,$DA,$FF,$46,$23,$56,$23,$4E,$3E,$01,$CD,$03,$C9 + DB $AF,$C9,$CD,$B3,$CD,$FE,$02,$28,$04,$CD,$E7,$C8,$C9,$79 + DB $D6,$20,$5F,$3A,$DA,$FF,$57,$3A,$D3,$FF,$E6,$60,$B2,$32 + DB $D3,$FF,$4F,$06,$0A,$CD,$FC,$C8,$4B,$06,$0B,$CD,$FC,$C8 + DB $AF,$C9,$CD,$B3,$CD,$FE,$04,$28,$04,$CD,$E7,$C8,$C9,$79 + DB $D6,$20,$5F,$3E,$4F,$BB,$38,$38,$21,$DA,$FF,$46,$23,$7E + DB $FE,$18,$30,$2E,$4F,$23,$56,$79,$B8,$38,$27,$7B,$BA,$38 + DB $23,$21,$CD,$FF,$71,$23,$70,$23,$73,$23,$72,$3E,$01,$32 + DB $C9,$FF,$3E,$50,$93,$5F,$7A,$83,$21,$D1,$FF,$CB,$5E,$28 + DB $01,$87,$32,$C8,$FF,$CD,$F7,$CB,$AF,$C9,$CD,$B3,$CD,$FE + DB $02,$28,$04,$CD,$E7,$C8,$C9,$79,$D6,$20,$4F,$3E,$4F,$B9 + DB $38,$15,$3A,$DA,$FF,$FE,$19,$30,$0E,$47,$32,$CB,$FF,$79 + DB $32,$CA,$FF,$CD,$D7,$C6,$CD,$02,$C7,$AF,$C9,$CD,$B3,$CD + DB $79,$D6,$20,$4F,$3E,$4F,$B9,$38,$0E,$3A,$CB,$FF,$47,$79 + DB $32,$CA,$FF,$CD,$D7,$C6,$CD,$02,$C7,$AF,$C9,$CD,$B3,$CD + DB $FE,$04,$28,$04,$CD,$E7,$C8,$C9,$79,$D6,$20,$5F,$21,$DA + DB $FF,$46,$23,$4E,$23,$56,$3A,$D2,$FF,$CD,$03,$C9,$AF,$C9 + DB $01,$80,$07,$DD,$E5,$E1,$CD,$7B,$C7,$3A,$D2,$FF,$57,$1E + DB $20,$CD,$FB,$C6,$7E,$A2,$20,$09,$36,$00,$CD,$7B,$C7,$73 + DB $CD,$7B,$C7,$23,$0B,$78,$B1,$20,$EA,$CD,$84,$C7,$AF,$C9 + DB $3A,$D0,$FF,$4F,$3A,$CE,$FF,$47,$3A,$CF,$FF,$5F,$3A,$CD + DB $FF,$57,$CD,$EB,$C7,$CD,$F7,$CB,$C9,$3A,$CE,$FF,$47,$3A + DB $D0,$FF,$4F,$CD,$D7,$C6,$CD,$02,$C7,$78,$32,$CB,$FF,$79 + DB $32,$CA,$FF,$AF,$C9,$CD,$4A,$C7,$3E,$01,$32,$C8,$FF,$3E + DB $4F,$32,$CF,$FF,$CD,$A5,$C8,$AF,$C9,$06,$00,$0E,$00,$C5 + DB $CD,$D0,$CD,$4A,$D5,$CD,$A0,$FF,$D1,$C1,$CB,$5B,$28,$0D + DB $0C,$79,$FE,$50,$28,$0D,$C5,$0E,$20,$CD,$A0,$FF,$C1,$0C + DB $79,$FE,$50,$20,$DE,$C5,$0E,$0D,$CD,$A0,$FF,$0E,$0A,$CD + DB $A0,$FF,$C1,$04,$78,$FE,$18,$20,$CA,$AF,$C9,$CD,$B3,$CD + DB $79,$E6,$0F,$07,$07,$07,$07,$47,$3A,$D1,$FF,$E6,$0F,$B0 + DB $32,$D1,$FF,$AF,$C9,$CD,$B3,$CD,$79,$FE,$30,$28,$0E,$FE + DB $31,$28,$1C,$FE,$32,$28,$3A,$FE,$33,$28,$4B,$AF,$C9,$3A + DB $CB,$FF,$47,$57,$3A,$CA,$FF,$4F,$3A,$CF,$FF,$5F,$CD,$EB + DB $C7,$AF,$C9,$3A,$CE,$FF,$47,$3A,$CB,$FF,$32,$CE,$FF,$3A + DB $C9,$FF,$4F,$3E,$01,$32,$C9,$FF,$C5,$CD,$14,$C6,$C1,$78 + DB $32,$CE,$FF,$79,$32,$C9,$FF,$AF,$C9,$3A,$CB,$FF,$47,$3A + DB $CA,$FF,$4F,$3A,$CD,$FF,$57,$3A,$CF,$FF,$5F,$CD,$EB,$C7 + DB $AF,$C9,$3A,$CE,$FF,$47,$3A,$CB,$FF,$4F,$3A,$CD,$FF,$B9 + DB $28,$16,$79,$32,$CE,$FF,$C5,$CD,$16,$C8,$C1,$78,$32,$CE + DB $FF,$3A,$D0,$FF,$4F,$CD,$7B,$CB,$AF,$C9,$47,$57,$3A,$CF + DB $FF,$5F,$3A,$D0,$FF,$4F,$CD,$EB,$C7,$18,$E8,$CD,$B3,$CD + DB $FE,$02,$D2,$66,$CD,$79,$FE,$30,$28,$0E,$FE,$31,$28,$0E + DB $FE,$34,$28,$15,$FE,$35,$28,$4A,$AF,$C9,$06,$19,$18,$02 + DB $06,$18,$3E,$06,$D3,$A0,$78,$D3,$A1,$AF,$C9,$2A,$F4,$BF + DB $EB,$06,$18,$0E,$00,$CD,$D7,$C6,$3A,$EA,$BF,$4F,$06,$46 + DB $78,$32,$DA,$FF,$CD,$FB,$C6,$1A,$77,$CD,$7B,$C7,$71,$CD + DB $84,$C7,$13,$23,$10,$F0,$3A,$DA,$FF,$B7,$28,$0C,$06,$0A + DB $3A,$EB,$BF,$4F,$AF,$32,$DA,$FF,$18,$DE,$AF,$C9,$79,$FE + DB $0D,$28,$26,$3A,$D9,$FF,$FE,$01,$28,$04,$CD,$93,$CD,$C9 + DB $06,$18,$0E,$00,$CD,$D7,$C6,$22,$DA,$FF,$3E,$02,$32,$D9 + DB $FF,$06,$46,$0E,$20,$CD,$FB,$C6,$71,$23,$10,$F9,$C9,$AF + DB $C9,$47,$3C,$32,$D9,$FF,$2A,$DA,$FF,$CD,$FB,$C6,$71,$3A + DB $EB,$BF,$CD,$7B,$C7,$77,$CD,$84,$C7,$23,$22,$DA,$FF,$78 + DB $FE,$47,$C0,$AF,$C9,$3A,$D9,$FF,$B7,$C0,$3C,$32,$D9,$FF + DB $E1,$C9,$CD,$80,$C6,$CD,$D7,$C6,$CD,$FB,$C6,$72,$CD,$7B + DB $C7,$73,$CD,$84,$C7,$C9,$CD,$80,$C6,$CD,$D7,$C6,$CD,$FB + DB $C6,$56,$CD,$7B,$C7,$5E,$CD,$84,$C7,$C9 + REPT 538 + nop ;[2be2] 00 + ENDR + ; 1.01 from system ROM + DB $31,$2e,$30,$31 ;[2dfc] + ;[2e00] + REPT 1024 + nop ;[2e02] + ENDR + REPT 63 + DB $ff + DB $00 + ENDR + DB $ff + DB $d1 + REPT 63 + DB $ff + DB $00 + ENDR + DB $ff + DB $2f ;[32ff] diff --git a/applications/SLF80037.relocated.asm b/applications/SLF80037.relocated.asm new file mode 100644 index 0000000..b9fba9a --- /dev/null +++ b/applications/SLF80037.relocated.asm @@ -0,0 +1,206 @@ + org $b821 + + ; Empty, it's populated directly by the SLF80037 main code +scancode_table: + REPT 640 ;[b821] + nop + ENDR + + ; Local variables for the "accented letter print" routine +alp_table: ;[baa1] + DB $D0,$5C,$00 + DB $D1,$7E,$00 + DB $D2,$7B,$00 + DB $D3,$7D,$00 + DB $D4,$40,$00 + DB $D5,$7C,$00 + DB $D6,$7E,$61 + DB $D7,$7E,$65 + DB $D8,$7E,$69 + DB $D9,$7E,$6F + DB $DA,$7E,$75 + DB $DB,$5E,$61 + DB $DC,$5E,$65 + DB $DD,$5E,$69 + DB $DE,$5E,$6F + DB $DF,$5E,$75 + DB $E0,$5B,$00 + DB $E1,$5C,$00 + DB $E2,$5D,$00 + DB $00 + +dkh_table: ;[badb] + ; vowel,circumflex,umlaut + DB $61, $DB, $D6 ; a + DB $65, $DC, $D7 ; e + DB $69, $DD, $D8 ; i + DB $6F, $DE, $D9 ; o + DB $75, $DF, $DA ; u + DB $00 + +dkh_scratch: ;[baeb] + DB $00 + +; Dead key handling +; The routine takes c as parameter, which is the input char, and leaves the eventually +; modified char in c as output. Carry is set if the output char should not be printed +; (i.e. when a modifier key is pressed). +; dkh_scratch is used as dead key status, deafult is 0 (no modifier key pressed and c itself +; is returned). +; There are two possible modifier keys that can change the output status: c=$f1 (up arrow) and +; c=$f2 (down arrow). +; If a modifier key is pressed, dkh_scratch is set to a value other than zero and carry is set: +; this changes the behavior of the routine at the next call: +; if the next pressed char is in dkh_table, then +; - left value of the table is returned in c if dkh_scratch is 1 (circumflex modifier). +; - right value of the table is returned in c if dkh_scratch is 2 (umlaut modifier). +; - the unchanged character is returned if it's not in dkh_table. +; At the end, dkh_scratch is clear. +deadKeyHandler: ;[baec] + ld a,c + cp $f1 + jr z,dkh_isf1 + cp $f2 + jr nz,dkh_nof1f2 + ld a,$02 ; if $f2, write 2 in dkh_scratch and return + jr dkh_f1f2_epilogue +dkh_isf1: + ld a,$01 ; if $f1, write 1 in dkh_scratch and return +dkh_f1f2_epilogue: + ld (dkh_scratch),a + scf ; carry set + ret + ; Anything but f1 and f2 +dkh_nof1f2: + ld a,(dkh_scratch) + or a + jr z,dkh_return + ld hl,dkh_table + ld b,$05 +dkh_loop: + ld a,(hl) + cp c + jr z,dkh_found + inc hl + inc hl + inc hl + djnz dkh_loop +epilogue: + xor a + ld (dkh_scratch),a ; clear dkh_scratch +dkh_return: + ret +dkh_found: + ld a,(dkh_scratch) + ld b,$00 + ld c,a + add hl,bc + ld c,(hl) ; c = hl + dkh_scratch + jr epilogue ; where hl is the pointer to found + + +; Briefly, this routine takes c as parameter. +; It has some kind of memory, since it uses: +; - alp_scratch as internal status +; - the jp at the entrypoint as trampoline, since its jp address is altered at runtime! +; By default, alp_entrypoint1 is used as trampoline jp address. +; Following this branch, c value is used to find an entry in the alp_table table. +; If found, left value of the entry is returned in c, right value in a. +; A pointer to the the right value is stored in the alp_scratch. +; If the right value is zero, the routine returns. +; If the right value is not zero, trampoline is altered enabling the secondary entrypoint. +; If the entrypoint has been altered, the next calls follow a fixed pattern: +; - alp_entrypoint2: c cargument is returned as is, $08 is returned in a and +; trampoline is changed to alp_entrypoint3. +; - alp_entrypoint3: the right value that triggered the alternate path is loaded in c (via alp_scratch). +; a is zeroed and path is restored to the default entrypoint. +accentedLetterPrintHandler: ;[bb23] + jp alp_entrypoint1 ; this jp is altered at runtime! +alp_entrypoint1: + ld a,c + bit 7,a + ret z ; return if bit 7 is cleared + ld hl,alp_table +alp_loop: + ld a,(hl) + or a + ret z + cp c + inc hl + jr z,alp_found + inc hl + inc hl + jr alp_loop +alp_found: + ld c,(hl) + inc hl + ld (alp_scratch),hl + ld a,(hl) + or a + ret z + ld a,c + ld de,alp_entrypoint2 ; change the entrypoint to the first stage alternate one + jr alp_epilogue + +alp_entrypoint2: ; first stage alternate entrypoint + ld a,$08 + ld de,alp_entrypoint3 ; change the entrypoint to the second stage alternate one + jr alp_epilogue + +alp_entrypoint3: ; second stage alternate entrypoint + ld hl,(alp_scratch) + ld c,(hl) ; take back the "right value" that triggered the alternate path + xor a + ld de,alp_entrypoint1 ; restore the default entrypoint +alp_epilogue: + ld (accentedLetterPrintHandler+1),de + or a ; set flags according to a content + ret ; result is in a + +alp_scratch: + DW $0000 + +; This function is indirectly called by ROM's putchar during second stage escaping +custom_escape: + ret ;[bb5d] + + REPT 1164 + nop + ENDR + + DB "TT" ;[bfea] + DB 00 + DB 00 + DB 00 + DB 00 + DB 00 + DB 00 + + ; pointer to the scancode table, used by CP/M bios routine to map + ; keystrokes against ASCII characters +pScancode_table: ;[bff2] + DW scancode_table + + ; Pointer to a sequence of fixed length strings, defined in SLF80037.COM. + ; They are used for function key shortcuts. + ; This pointer is initialized by SLF80037.COM. +pShortcuts_base: + DB 0, 0 ;[bff4] + + ; Dead key handler pointer + ; referenced in CP/M BIOS when reading from kbd +pDeadKeyHandler: ;[bff6] + DW deadKeyHandler + ; (TODO) Custom character print handler + ; referenced in CP/M BIOS when writing to printer +pAccentedLetterPrintHandler: ;[bff8] + DW accentedLetterPrintHandler + + ; Pointer to a custom routine used to handle escape during putchar. + ; The routine is implemented here, and at the moment is just a ret. + ; At the moment we don't know much about it, except that it is used by + ; the ROM, so using putchar when CP/M is not initialized may lead to + ; unexpected behavior! +pCustom_escape: ;[bffa] + DW custom_escape + ;[bffc] diff --git a/applications/TRX62.COM.asm b/applications/TRX62.COM.asm new file mode 100644 index 0000000..ca3f84a --- /dev/null +++ b/applications/TRX62.COM.asm @@ -0,0 +1,881 @@ +; z80dasm 1.1.6 +; command line: z80dasm -t -a -l ../from-cpmls/trx62.com + +FILE_LIST := $0948 + +IO_SIO_ADDR := $b0 + + org $0100 + +entrypoint: + nop ;[0100] they like very much to lose time + nop ;[0101] + nop ;[0102] + jp init ;[0103] + +SIO_SETUP_WRAPPER: + jp SIO_SETUP ;[0106] +SIO_WRITE_WRAPPER: + jp SIO_WRITE ;[0109] +SIO_READ_WRAPPER: + jp SIO_READ ;[010c] + +init: + ld sp,stack_base ;[010f] initialize stack pointer + call SIO_SETUP_WRAPPER ;[0112] Setup SIO + ld hl,($0001) ;[0115] Take the BIOS entrypoint, aka base address of BIOS service routines + ld de,$0006 ;[0118] +6 displacement... + add hl,de ;[011b] ... aka syscall #2, aka console input (CONIN) + ld (trampoline_addr+1),hl ;[011c] + call PRINT_NEXT_STR ;[011f] cd 20 03 . . + DB "\r\n" + DB " " + DB "File exchange program vers 6.0 ,4800 Baud (SED)" + DB "\r\n" + DB $00 + jp main ;[0160] + +; BEGIN stack area + REPT 64 + DB $00 + ENDR +stack_base: +; END stack area + +; Counter of files to be transferred (after wildcard expansion) +num_of_files: + DB 0 + +; buffer area used by the getline routine +GETLINE_BUFSIZE := 76 +getline_buffer: + REPT GETLINE_BUFSIZE + DB $00 + ENDR + DB $20 + +drive_index: + DB $00 + +; BEGIN: getline +; This routine populates the getline_buffer area with character until a newline. +; Backspace is handled to delete inserted chars. +; Only printable characters are allowed, and all alphabetic chars are made uppercase. +; (This leads to funny side effects since some symbols may be altered) +getline: + call PRINT_NEXT_STR ;[01f2] + DB "\r\n" + DB "*>" + DB 0 + ld b,GETLINE_BUFSIZE ;[01fa] + ld hl,getline_buffer+GETLINE_BUFSIZE ;[01fc] + getline_init_buf: ; initialize the buffer area filling it with spaces + dec hl ;[01ff] + ld (hl),' ' ;[0200] + djnz getline_init_buf ;[0202] + xor a ;[0204] +getline_out_loop: + push af ;[0205] +getline_in_loop: + call guard_bios_conin ;[0206] console input + and $7f ;[0209] Mask to get only chars in ASCII table + cp $03 ;[020b] ETX + jp z,epilogue ;[020d] terminate application + cp '\r' ;[0210] + jp z,handle_newline ;[0212] end the getline method and return buffer pointer + cp $7f ;[0215] DEL + jp z,handle_delete ;[0217] + cp $08 ;[021a] Backspace + jp z,handle_delete ;[021c] + cp ' ' ;[021f] first printable (valid) ASCII char + jp c,handle_non_printable ;[0221] if non printable... + cp '~' ;[0224] last printable (valid) ASCII char + jp nc,handle_non_printable ;[0226] if non printable... just beep and wait for another char + cp 'A' ;[0229] + jp c,is_symbol ;[022b] if less than 'A', skip (i.e. symbols or numbers) + and $5f ;[022e] else, transform lowercase chars to uppercase (and change some symbols... side effect) + is_symbol: ; is symbol or number, just store as is + ld (hl),a ;[0230] store char + call bdos_write ;[0231] then print it + pop af ;[0234] + inc a ;[0235] + cp 76 ;[0236] + jp z,getline_ovf ;[0238] if buffer space for filenames is ended, terminate + inc hl ;[023b] + jp getline_out_loop ;[023c] + +; Buffer area overflowed. Print error message and start back with line read +getline_ovf: + call PRINT_NEXT_STR ;[023f] + DB " ??" + DB 0 + jp getline ;[0246] start back reading a new line + +handle_non_printable: + ld a,$07 ;[0249] BEL + call bdos_write ;[024b] just beep + jp getline_in_loop ;[024e] and then continue acquiring chars + +handle_delete: + pop af ;[0251] retrieve number of elements in buffer + cp $00 ;[0252] if start of buffer, ... + jp z,getline_out_loop ;[0254] ... just ignore delete and go on + dec hl ;[0257] else, move buffer pointer back + dec a ;[0258] and number of elements + push af ;[0259] + ld (hl),' ' ;[025a] Clear the inserted char + ld a,$08 ;[025c] print backspace (move cursor back) + call bdos_write ;[025e] + ld a,$20 ;[0261] print space to overwrite char on the screen + call bdos_write ;[0263] + ld a,$08 ;[0266] move cursor back again + call bdos_write ;[0268] + jp getline_in_loop ;[026b] + +;; Terminate the filename fetch loop +handle_newline: + ld hl,getline_buffer ;[026e] + pop af ;[0271] + ret ;[0272] + +;; END of getline routine + +; argument parser +; parse any filename passed as arguments to the program. +; Note 1: wildcard are supported +; FILE* -> all files starting by FILE, regardless of the extension +; FILE*.EXT -> all files starting by FILE, with EXT extension +; FILE.E* -> all files named FILE with extension starting by E +; Note 2: if drive is specified, the drive letter MUST be uppercase! +arg_parser: + push de ;[0273] + call fcb_clear ;[0274] clear the FCB1 data structure + inc hl ;[0277] skip first char to check if drive is specified or just filename + ld a,(hl) ;[0278] the second char may be ':', indicating that a drive letter was specified + dec hl ;[0279] restore pointer to beginning of argument string + cp ':' ;[027a] check if drive letter is specified + ld a,$00 ;[027c] preload default drive index + jp nz,arg_no_drive ;[027e] if no drive letter specified, jump to filename parsing + ld a,(hl) ;[0281] load the drive letter (1st char of the argument) + cp 'A' ;[0282] + jp c,invalid_file_name ;[0284] less than A is invalid + cp 'E' ;[0287] + jp nc,invalid_file_name ;[0289] bigger than E is invalid + and $0f ;[028c] get the drive index from the letter + inc hl ;[028e] skip drive letter + inc hl ;[028f] +arg_no_drive: + ld (drive_index),a ;[0290] store drive number (default one or specified with D: syntax) + ld (de),a ;[0293] load drive index in DR byte of FCB + inc de ;[0294] move pointer to FCB to filename + ld b,$08 ;[0295] + fcb_fname_loop: ; copy all the filename chars from the argument to FCB + ld a,(hl) ;[0297] + cp '*' ;[0298] check if wildcard char + jp z,fcb_fname_is_wildcard ;[029a] + cp '.' ;[029d] if a dot is encountered, switch to filetype parsing + jp z,fcb_fname_is_filetype ;[029f] + ld (de),a ;[02a2] copy char in FCB and move pointers + inc hl ;[02a3] + inc de ;[02a4] + djnz fcb_fname_loop ;[02a5] go on with file name reading + ld a,(hl) ;[02a7] ...filename has ended, + cp '.' ;[02a8] check if filetype is specified + jp nz,fcb_fname_no_filetype ;[02aa] + fcb_fname_is_filetype: ; filetype parsing section + ex de,hl ;[02ad] save in de the current pointer in args string + pop hl ;[02ae] restore the initial FCB pointer in hl + push hl ;[02af] + ld bc,9 ;[02b0] + add hl,bc ;[02b3] shift FCB pointer to first filetype char + ex de,hl ;[02b4] hl = pointer to '.' char of arguments +; de = pointer to T1 byte in FCB + fcb_parse_ftype: ; copy all the filetype chars from the argument to FCB + ld b,3 ;[02b5] number of bytes composing filetype + inc hl ;[02b7] skip '.' +fcb_ftype_loop: + ld a,(hl) ;[02b8] + cp '*' ;[02b9] + jp z,fcb_ftype_is_wildcard ;[02bb] wildcard on filetype + ld (de),a ;[02be] copy char in FCB + inc hl ;[02bf] update pointers + inc de ;[02c0] + djnz fcb_ftype_loop ;[02c1] continue copying filetype in FCB + pop de ;[02c3] + xor a ;[02c4] + ret ;[02c5] + +fcb_ftype_is_wildcard: + ld a,'?' ;[02c6] fill the remaining chars of filetype with '?' + ld (de),a ;[02c8] + inc de ;[02c9] + djnz fcb_ftype_is_wildcard ;[02ca] + pop de ;[02cc] + xor a ;[02cd] + ret ;[02ce] + +fcb_fname_is_wildcard: + ld a,'?' ;[02cf] fill the remaining chars of filename with '?' + ld (de),a ;[02d1] + inc de ;[02d2] + djnz fcb_fname_is_wildcard ;[02d3] + inc hl ;[02d5] check if, after wildcard char, a filetype is specified + ld a,(hl) ;[02d6] + cp '.' ;[02d7] + jp z,fcb_parse_ftype ;[02d9] + fcb_fname_no_filetype: ; no filetype specified + ld b,$03 ;[02dc] + ld a,' ' ;[02de] clear filetype bytes +fcb_clear_filetype: + ld (de),a ;[02e0] + inc de ;[02e1] + djnz fcb_clear_filetype ;[02e2] + pop de ;[02e4] restore FCB pointer + xor a ;[02e5] + ret ;[02e6] + +invalid_file_name: + call PRINT_NEXT_STR ;[02e7] + DB "\r\n" + DB "Invalid filename" + DB $00 + ld a,$ff ;[02fd] + pop de ;[02ff] + ret ;[0300] + +; Clear file name in fcb +; Arguments: +; de: pointer to FCB1 (File Control Block 1) +fcb_clear: + push de ;[0301] + xor a ;[0302] + ld (de),a ;[0303] Set drive number to 0 (default) + inc de ;[0304] Move pointer to filename in FCB + ld b,11 ;[0305] Number of characters in filename (8+3) + ld a,' ' ;[0307] +fcb_clear_filename: + ld (de),a ;[0309] clear the filename (write spaces) + inc de ;[030a] + djnz fcb_clear_filename ;[030b] + xor a ;[030d] + ld b,21 ;[030e] Number of bytes of the rest of the FCB structure +fcb_clear_flags: + ld (de),a ;[0310] + inc de ;[0311] + djnz fcb_clear_flags ;[0312] + pop de ;[0314] + ret ;[0315] + +; Self-changing code (yaaay!) +; The call address is changed in init, then it calls the +; CONIN BIOS routine +guard_bios_conin: + push bc ;[0316] + push de ;[0317] + push hl ;[0318] +trampoline_addr: + call $0000 ;[0319] + pop hl ;[031c] + pop de ;[031d] + pop bc ;[031e] + ret ;[031f] + +PRINT_NEXT_STR: + ex (sp),hl ;[0320] + ld a,(hl) ;[0321] + inc hl ;[0322] + ex (sp),hl ;[0323] + or a ;[0324] + ret z ;[0325] + call bdos_write ;[0326] + jp PRINT_NEXT_STR ;[0329] + +; Send the character in A to the screen +bdos_write: + push af ;[032c] + push hl ;[032d] + push bc ;[032e] + ld c,$02 ;[032f] + ld e,a ;[0331] + call $0005 ;[0332] + pop bc ;[0335] + pop hl ;[0336] + pop af ;[0337] + ret ;[0338] + +; Open the file specified in FCB (pointer passed in DE) +bdos_fopen: + ld a,$00 ;[0339] + ld hl,$0020 ;[033b] CR byte offset + add hl,de ;[033e] + ld (hl),a ;[033f] zero the value of CR in FCB + ld c,$0f ;[0340] + call $0005 ;[0342] + cp $ff ;[0345] + ret nz ;[0347] return on success + push af ;[0348] else print error message + call PRINT_NEXT_STR ;[0349] + DB "\r\n" + DB "File open error" + Db 0 + pop af ;[035e] + ret ;[035f] + +bdos_fread: + push hl ;[0360] + push de ;[0361] + push bc ;[0362] + ld c,$14 ;[0363] + call $0005 ;[0365] + pop bc ;[0368] + pop de ;[0369] + pop hl ;[036a] + ret ;[036b] + +send_record: + push bc ;[036c] + ld hl,$0080 ;[036d] record buffer address + ld b,$80 ;[0370] record size + send_record_loop: ; just load bytes from buffer and send them + ld a,(hl) ;[0372] + call SIO_WRITE_WRAPPER ;[0373] + inc hl ;[0376] + djnz send_record_loop ;[0377] + pop bc ;[0379] + ret ;[037a] + +send_file: + ld b,$80 ;[037b] for each 128 blocks, ask ACK + call bdos_fread ;[037d] read first record from opened file + call send_record ;[0380] send the record content over serial + dec b ;[0383] +send_file_loop: + push bc ;[0384] + call bdos_fread ;[0385] read next record + or a ;[0388] on error, file is finished: terminate + jp nz,send_file_end ;[0389] + ld a,$17 ;[038c] ETB (end of transfer block) + call SIO_WRITE_WRAPPER ;[038e] between each record, send ETB + call send_record ;[0391] + pop bc ;[0394] + djnz send_file_loop ;[0395] repeat until end of file or max num of record + ld a,$17 ;[0397] send ETB for last record + call SIO_WRITE_WRAPPER ;[0399] + call SIO_READ_WRAPPER ;[039c] read ACK + cp $06 ;[039f] + jp nz,send_file_fail ;[03a1] if other than ACK, terminate with error + jp send_file ;[03a4] else start back sending other records +; note: after 128 record, sequence is -> ETB; <- ACK -> ETB -> new record + +send_file_end: + ld a,$04 ;[03a7] EOT (end of transmission) + call SIO_WRITE_WRAPPER ;[03a9] + pop bc ;[03ac] + xor a ;[03ad] + ret ;[03ae] + +send_file_fail: + ld a,$ff ;[03af] + ret ;[03b1] return failure + +; Given the filename pattern specified in FCB, process the files to be copied. +; If wildcards were specified, they must be expanded, i.e. replaced with the +; matching filenames. +; The list of expanded filename is stored in a separate buffer, using a particular +; format. For each filename 16 bytes are reserved, as specified in push_file_in_list +; routine description. +expand_wildcard: + xor a ;[03b2] + ld (num_of_files),a ;[03b3] reset the number of files to be sent + ld hl,FILE_LIST ;[03b6] + ld (p_file_list),hl ;[03b9] store the pointer to the file list area + call bdos_sfirst ;[03bc] start finding files matching the one(s) in FCB +; an offset to the directory entry is returned in a + cp $ff ;[03bf] check if error returned + jp z,perror_no_file ;[03c1] + call push_file_in_list ;[03c4] copy the filename from the directory entry (a) into the list to be sent + ld a,(num_of_files) ;[03c7] + inc a ;[03ca] + ld (num_of_files),a ;[03cb] +fetch_files_loop: + call bdos_snext ;[03ce] try finding next file + cp $ff ;[03d1] + jp z,no_more_files ;[03d3] no more files to be retrieved + call push_file_in_list ;[03d6] copy the filename from the directory entry (a) into the list to be sent + ld a,(num_of_files) ;[03d9] + inc a ;[03dc] + ld (num_of_files),a ;[03dd] + jp fetch_files_loop ;[03e0] + +; Pointer to the files to be sent list +p_file_list: + DW $0000 + +; Push an entry in the mysterious file_list array. +; Each entry is 16 byte long, containing the following info +; _ A B : F F F F F F F F T T T _ +; Where: +; _ is a space character +; AB is the file index (starting from "00") +; : is literally ':' +; F is file name, according to CP/M specs +; T is file type, according to CP/M specs +; This curious format is used both for retrieving the filenames and to display +; the file list on the screen. +push_file_in_list: + call find_dir_entry ;[03e5] use the directory entry offset passed in a to fetch filename (returned in hl) + ld de,(p_file_list) ;[03e8] + ld a,$20 ;[03ec] + ld (de),a ;[03ee] + inc de ;[03ef] + ld a,(num_of_files) ;[03f0] + call to_integer ;[03f3] compute and store file index (increasing value from 00) + ld a,':' ;[03f6] add the separator character + ld (de),a ;[03f8] + inc de ;[03f9] + ld bc,11 ;[03fa] copy file name and type (hl) into file list entry + ldir ;[03fd] repeat (de++) = (hl++) until --bc == 0 + ld a,$20 ;[03ff] add last space + ld (de),a ;[0401] + inc de ;[0402] + ld (p_file_list),de ;[0403] point to a new entry + ret ;[0407] + +; Given return value of BDOS disk functions, find the directory entry and +; return the filename +find_dir_entry: + and $03 ;[0408] a can be 0-3 + add a,a ;[040a] + add a,a ;[040b] + add a,a ;[040c] + add a,a ;[040d] + add a,a ;[040e] a = a * 32 + ld hl,$0080 ;[040f] dma offset + ld b,$00 ;[0412] + ld c,a ;[0414] + add hl,bc ;[0415] directory entry = dma_offset + a * 32 + inc hl ;[0416] skip User number (UU) and go to filename + ret ;[0417] + +; Convert value in a to a two digit integer string. +; Store ascii digits in memory pointed by de +to_integer: + ld b,$ff ;[0418] +divide_loop: + inc b ;[041a] + sub $0a ;[041b] + jp nc,divide_loop ;[041d] b = a/10, a = a%10 - 10 + add a,$3a ;[0420] get a printable reminder in range '0'-'9' (units) + push af ;[0422] + ld a,b ;[0423] + add a,$30 ;[0424] get a printable quotient in range '0'-'9' (tens) + ld (de),a ;[0426] store tens digit + inc de ;[0427] + pop af ;[0428] + ld (de),a ;[0429] store units digit + inc de ;[042a] + ret ;[042b] + +perror_no_file: + call PRINT_NEXT_STR ;[042c] cd 20 03 . . + DB "\r\n" + DB "File does not exist" + DB 0 + ld a,$ff ;[0445] 3e ff > . + ret ;[0447] c9 . + +no_more_files: + ld de,(p_file_list) ;[0448] + xor a ;[044c] + ld (de),a ;[044d] terminate the list by adding a "null" element with first byte set to zero + ld de,$0948 ;[044e] restore pointer to the first element of the list + ld (p_file_list),de ;[0451] + ret ;[0455] + +; Search for the first occurrence of the specified file (specified in FCB). +; The filename can include ? marks, which match anything on disc. +; Returns A=0FFh if error, or A=0-3 if successful. The value returned can be +; used to calculate the address of a memory image of the directory entry; it is +; to be found at DMA+A*32. +bdos_sfirst: + ld c,$11 ;[0456] + jp $0005 ;[0458] + +; Finds the next occurrence of the specified file after the one returned last +; time. Same considerations as bdos_sfirst. +bdos_snext: + ld c,$12 ;[045b] + jp $0005 ;[045d] + +list_files_to_send: + ld b,$00 ;[0460] + ld hl,$0948 ;[0462] point to first element in file list +print_file_to_send_loop: + push bc ;[0465] + ld a,(hl) ;[0466] + cp $01 ;[0467] + jp nz,print_no_skip ;[0469] + ld bc,$0010 ;[046c] +print_skip_loop: + add hl,bc ;[046f] 09 . + ld a,(hl) ;[0470] 7e ~ + cp $01 ;[0471] skip entries marked with 1 + jp z,print_skip_loop ;[0473] +print_no_skip: + cp $00 ;[0476] null element, terminate + jp z,print_file_to_send_end ;[0478] + ld b,12 ;[047b] + print_file_loop: ; print first 12 characters of the file entry, aka _AB:FFFFFFFF + ld a,(hl) ;[047d] + call bdos_write ;[047e] + inc hl ;[0481] + djnz print_file_loop ;[0482] + ld a,'.' ;[0484] print separator + call bdos_write ;[0486] + ld b,4 ;[0489] + print_file_type_loop: ; print the last 4 bytes of the entry, aka TTT_ + ld a,(hl) ;[048b] + call bdos_write ;[048c] + inc hl ;[048f] + djnz print_file_type_loop ;[0490] + pop bc ;[0492] + inc b ;[0493] + ld a,b ;[0494] + and $03 ;[0495] + jp nz,print_file_to_send_loop ;[0497] print up to four file names per row + call PRINT_NEXT_STR ;[049a] + DB "\r\n" + DB 0 + jp print_file_to_send_loop ;[04a0] + +print_file_to_send_end: + pop bc ;[04a3] c1 . + ret ;[04a4] c9 . + +ask_files_to_skip: + call PRINT_NEXT_STR ;[04a5] + DB "\r\n\n" + DB "Are there any files not to be transfered? (type file number)" + DB "\r\n" + DB "more than one separated by ," + DB "\r\n" + DB "or RETURN to continue" + DB "\r\n" + DB 0 + call getline ;[051f] prompt and fetch the indexes of files to be skipped + cp $00 ;[0522] + ret z ;[0524] no files to be skipped, return + call mark_sk_files ;[0525] else, add a mark to each file to be skipped in the file list + call PRINT_NEXT_STR ;[0528] print the updated files list + DB "\r\n" + DB " " + Db "FILES TO SEND" + DB "\r\n\n" + DB 0 + call list_files_to_send ;[0553] + jp ask_files_to_skip ;[0556] + + ret ;[0559] + +; mark files to skip in file list +; note: numbers have to be written two digits with leading zeros! +; i.e. 00 01 10 ... +mark_sk_files: + ld a,(hl) ;[055a] first digit (msb) + call isnum ;[055b] + cp $ff ;[055e] + jp z,mark_sk_invalid ;[0560] invalid character, ignore this and next one and search separator (,) + and $0f ;[0563] get 0-9 value + rrc a ;[0565] + rrc a ;[0567] + rrc a ;[0569] + rrc a ;[056b] a <<= 4 + ld b,a ;[056d] + inc hl ;[056e] + ld a,(hl) ;[056f] second digit (lsb) + call isnum ;[0570] + cp $ff ;[0573] + jp z,ch_invalid ;[0575] invalid character, skip and search separator (,) + and $0f ;[0578] + or b ;[057a] get BCD value from the two inserted digit + call bcd_to_hex ;[057b] convert BCD value in an hex number + push hl ;[057e] + ld hl,$0948 ;[057f] file list pointer + ld b,a ;[0582] + ld a,(num_of_files) ;[0583] + dec a ;[0586] + cp b ;[0587] + jp c,index_invalid ;[0588] if requested number is greater than num of files, ignore this file number + ld de,$0000 ;[058b] + inc b ;[058e] + find_index_loop: ; find the requested entry + add hl,de ;[058f] + ld de,$0010 ;[0590] + djnz find_index_loop ;[0593] + ld (hl),$01 ;[0595] mark that entry as to be skipped +index_invalid: + pop hl ;[0597] restore pointer to digits inserted from keyboard + ch_invalid: ; skip separating char between file numbers + inc hl ;[0598] + ld a,(hl) ;[0599] + cp ' ' ;[059a] space terminates parsing + ret z ;[059c] + cp ',' ;[059d] comma is a separator, just skip it + jp nz,l05c8h ;[059f] why is this code duplicated ?!? + inc hl ;[05a2] + jp mark_sk_files ;[05a3] + +; given a char in a, returns the char itself if is a number, else 0xFF +isnum: + cp '0' ;[05a6] + jp c,l05aeh ;[05a8] + cp '9'+1 ;[05ab] + ret c ;[05ad] +l05aeh: + ld a,$ff ;[05ae] + ret ;[05b0] + +bcd_to_hex: + push bc ;[05b1] + push af ;[05b2] + and $0f ;[05b3] + ld c,a ;[05b5] + pop af ;[05b6] + and $f0 ;[05b7] + rrc a ;[05b9] + ld b,a ;[05bb] + rrc a ;[05bc] + rrc a ;[05be] + add a,b ;[05c0] + add a,c ;[05c1] result = msb * 16 / 2 + msb * 16 / 8 + lsb + pop bc ;[05c2] result = msb * 10 + lsb + ret ;[05c3] + +mark_sk_invalid: + inc hl ;[05c4] + jp ch_invalid ;[05c5] + + l05c8h: ; this seems the same code as in l0598h, this doesn't have much sense + inc hl ;[05c8] + ld a,(hl) ;[05c9] + cp ' ' ;[05ca] + ret z ;[05cc] + cp ',' ;[05cd] + dec hl ;[05cf] + jp z,ch_invalid ;[05d0] + inc hl ;[05d3] + jp l05c8h ;[05d4] + +pop_filelist_in_fcb: + call fcb_clear ;[05d7] clear the FCB structure + ld hl,(p_file_list) ;[05da] load pointer to the file list structure + ld a,(hl) ;[05dd] + cp $01 ;[05de] + jp nz,pop_filelist_no_skip ;[05e0] + ld bc,16 ;[05e3] skip files not to be sent + pop_filelist_skip_loop: ; continue skipping all files not to be sent + add hl,bc ;[05e6] + ld a,(hl) ;[05e7] + cp $01 ;[05e8] + jp z,pop_filelist_skip_loop ;[05ea] +pop_filelist_no_skip: + cp $00 ;[05ed] terminator of the file list, return + ld a,$ff ;[05ef] + ret z ;[05f1] + inc hl ;[05f2] + ld a,(hl) ;[05f3] copy both digits of file index in message to be printed + ld (file_index_str+2),a ;[05f4] + inc hl ;[05f7] + ld a,(hl) ;[05f8] + ld (file_index_str+3),a ;[05f9] + inc hl ;[05fc] skip separators + inc hl ;[05fd] + ld bc,11 ;[05fe] + ld a,(drive_index) ;[0601] copy drive index in FCB + ld (de),a ;[0604] + inc de ;[0605] + ldir ;[0606] Copy the filename in FCB + inc hl ;[0608] update pointer to the next entry + ld (p_file_list),hl ;[0609] save the pointer value + xor a ;[060c] return success + ret ;[060d] + +send_name: + ld a,$02 ;[060e] STX (start of text) + call SIO_WRITE_WRAPPER ;[0610] + ex de,hl ;[0613] + inc hl ;[0614] + ld b,11 ;[0615] filename+filetype length + send_name_loop: ; send the filename over serial (fixed length) + ld a,(hl) ;[0617] + call SIO_WRITE_WRAPPER ;[0618] + inc hl ;[061b] + djnz send_name_loop ;[061c] + ld a,$03 ;[061e] EXT (end of text) + call SIO_WRITE_WRAPPER ;[0620] + call SIO_READ_WRAPPER ;[0623] wait for char from serial + cp $06 ;[0626] ACK + jp nz,send_name_failed ;[0628] if others than ACK received, terminate + xor a ;[062b] else return ok + ret ;[062c] +send_name_failed: + ld a,$ff ;[062d] + ret ;[062f] + +main: + ld hl,$0080 ;[0630] Get number of characters in command tail + ld a,(hl) ;[0633] i.e. arguments + cp $00 ;[0634] if no arguments + jp z,cmd_no_args ;[0636] skip + inc hl ;[0639] TODO: why jump two char and not one? maybe one is a space? + inc hl ;[063a] + jp preface ;[063b] +cmd_no_args: + call getline ;[063e] prompt request and ask filename to user +preface: + ld de,$005c ;[0641] load pointer to default FCB1 + call arg_parser ;[0644] parse filename in argument and load it in FCB + or a ;[0647] check return value + jp nz,cmd_no_args ;[0648] if error (!=0), get filename from keyboard + call expand_wildcard ;[064b] gather the file names matching the requested ones in a separate list + or a ;[064e] + jp nz,cmd_no_args ;[064f] error in file gathering = ask (again) which files to send + call PRINT_NEXT_STR ;[0652] + DB "\r\n" + DB " " + DB "FILES FOUND ON DISK" + DB "\r\n\n" + DB 0 + call list_files_to_send ;[0682] + call ask_files_to_skip ;[0685] + call PRINT_NEXT_STR ;[0688] + DB "\r\n\n" + DB "Sending file" + DB "\r\n\n" + DB 0 +begin_file_send: + ld de,$005c ;[069e] load pointer to FCB1 + call pop_filelist_in_fcb ;[06a1] + or a ;[06a4] + jp nz,print_ok_transfer ;[06a5] on failure, just print success (?!?) + ld de,$005c ;[06a8] load pointer to FCB1 + call bdos_fopen ;[06ab] open file specified in FCB1 + jp z,perror_file_open ;[06ae] print error on failer + ld de,$005c ;[06b1] + call send_name ;[06b4] send the preamble and the filename over serial + or a ;[06b7] + jp nz,perror_send_name ;[06b8] + call PRINT_NEXT_STR ;[06bb] +file_index_str: + DB "* " + DB "00" + DB 0 + ld de,$005c ;[06c3] load pointer to FCB1 + call send_file ;[06c6] send the actual file over serial + or a ;[06c9] + jp nz,perror_transmit ;[06ca] + call SIO_READ_WRAPPER ;[06cd] + cp $06 ;[06d0] check for ACK at the end of transmission + jp z,begin_file_send ;[06d2] jump sending next file on success + call PRINT_NEXT_STR ;[06d5] else, print error string + DB "\r\n" + DB "Error on receiver (send next file or reboot Y/N)" + Db 0 + call guard_bios_conin ;[070b] + cp 'Y' ;[070e] + jp z,begin_file_send ;[0710] go to next file on 'Y' + jp epilogue ;[0713] terminate on other + +print_ok_transfer: + call PRINT_NEXT_STR ;[0716] + DB "\r\n\n" + DB "All files transfered (more files to send? Y/N)" + DB 0 + call guard_bios_conin ;[074c] + cp $59 ;[074f] fe 59 + jp z,cmd_no_args ;[0751] + jp epilogue ;[0754] + +perror_file_open: + call PRINT_NEXT_STR ;[0757] + DB "\r\n" + DB "File open error" + DB 0 + jp epilogue ;[076c] + +perror_send_name: + call PRINT_NEXT_STR ;[076f] + DB "\r\n" + DB "Name transfer error" + DB 0 + jp epilogue ;[0788] + +perror_transmit: + call PRINT_NEXT_STR ;[078b] + DB "\r\n" + DB "Transmission error" + DB 0 + jp epilogue ;[07a3] + +epilogue: + ld a,$18 ;[07a6] + call SIO_WRITE_WRAPPER ;[07a8] + call PRINT_NEXT_STR ;[07ab] + DB "\r\n\n" + DB "End of transmission" + DB 0 + jp $0000 ;[07c5] reset, terminate application + +; Sends to port $b1 data from $0458 to $0460 (SIO SETUP) +SIO_SETUP: + ld c,IO_SIO_ADDR+1 ;[07c8] Output port + ld b,$09 ;[07ca] Number of repetitions + ld hl,SIO_SETUP_COMMANDS ;[07cc] Starting address + otir ;[07cf] + ret ;[07d1] + +SIO_SETUP_COMMANDS: + DB $18 ; Reset channel 0 + DB $04 ; Access WR4 + DB $4c ; Parity Disabled, 2 stop bits (?), 8 bit sync,Data Rate x16 = Clock rate + DB $01 ; Access WR1 + DB $00 ; Disable all interrupts + DB $05 ; Access WR5 + DB $ea ; TX CRC disabled, RTS enabled, CRC-16 disabled, Transmit enabled, Send break disabled, 8 bits/character, dtr enabled + DB $03 ; Access WR3 + DB $c1 ; Receive enabled, the rest disabled, 8 bits/character + +; Write the character stored in the A register to port B of the SIO (Blocks until can transmit) +SIO_WRITE: + push af ;[07db] +SIO_WRITE_LOOP: + in a,(IO_SIO_ADDR+1) ;[07dc] + and $04 ;[07de] + jp z,SIO_WRITE_LOOP ;[07e0] + pop af ;[07e3] + out (IO_SIO_ADDR),a ;[07e4] + ret ;[07e6] + +; Receive a character from the SIO and stores it in A (it blocks until there is something available to read) +SIO_READ: + in a,(IO_SIO_ADDR+1) ;[07e7] + and $01 ;[07e9] + jp z,SIO_READ ;[07eb] + in a,(IO_SIO_ADDR) ;[07ee] + ret ;[07f0] + +; Junk + inc bc ;[07f1] 03 . + dec c ;[07f2] 0d . + ld a,(bc) ;[07f3] 0a . + ld c,(hl) ;[07f4] 4e N + ld h,c ;[07f5] 61 a + ld l,l ;[07f6] 6d m + ld h,l ;[07f7] 65 e + jr nz,$+118 ;[07f8] 20 74 t + ld (hl),d ;[07fa] 72 r + ld h,c ;[07fb] 61 a + ld l,(hl) ;[07fc] 6e n + ld (hl),e ;[07fd] 73 s + ld h,(hl) ;[07fe] 66 f + ld h,l ;[07ff] 65 e diff --git a/applications/md5sum.txt b/applications/md5sum.txt new file mode 100644 index 0000000..7c913da --- /dev/null +++ b/applications/md5sum.txt @@ -0,0 +1,8 @@ +22d027c28acc59b775c8a826194d9fc7 build/FUNK00.COM +2a75591c8b454c74df433ab1b58a47fd build/PAR8003.COM +5c6f140b44c03539fea2e30eab1bb138 build/PAR8003.relocated +8c0541188297d176ffa06d4cae53591f build/SLF80037.COM +b022bb2d88531e11fd4f6fc4c89f31ed build/SLF80037.relocated +2b5ea330d652f8da9cc47b3038dadbf9 build/COPY8003.COM +8924c5c5c9f22f56f06dc1f913a091a4 build/SG8003.COM +3cec9f3b89fd35aa1cd54a3997902085 build/TRX62.COM \ No newline at end of file diff --git a/cpm_bios.asm b/cpm_bios.asm index 44a5a2d..6e63c53 100644 --- a/cpm_bios.asm +++ b/cpm_bios.asm @@ -315,8 +315,8 @@ conout: call bank_switch_on ;[f3bf] ret ;[f3c2] - ld hl,($bff8) ;[f3c3] 2a f8 bf - jp (hl) ;[f3c6] e9 + ld hl,($bff8) ;[f3c3] Handle accented letters (relocated SLF80037.COM code) + jp (hl) ;[f3c6] ; Paper tape punch output ; Write the character in C to the "paper tape punch" - or whatever the current @@ -361,21 +361,21 @@ label_f3e2: list: ld hl,$0000 ;[f3e6] big TODO, which printer?? ld ($f471),hl ;[f3e9] - ld a,($0003) ;[f3ec] - cp $81 ;[f3ef] - call z,$f3ab ;[f3f1] + ld a,($0003) ;[f3ec] Fetch IOBYTE + cp $81 ;[f3ef] Check for LST=LPT, PUN=TTY, RDR=TTY, CON=CRT + call z,$f3ab ;[f3f1] in this case, just CONOUT label_f3f4: - ld a,($0003) ;[f3f4] + ld a,($0003) ;[f3f4] Fetch IOBYTE rlc a ;[f3f7] rlc a ;[f3f9] - and $03 ;[f3fb] + and $03 ;[f3fb] Mask LST only or a ;[f3fd] - jr z,label_f408 ;[f3fe] + jr z,label_f408 ;[f3fe] LST=0 - CONOUT dec a ;[f400] - jr z,label_f40b ;[f401] + jr z,label_f40b ;[f401] LST=1 - just return dec a ;[f403] - jr z,label_f40c ;[f404] - jr label_f432 ;[f406] + jr z,label_f40c ;[f404] LST=2 + jr label_f432 ;[f406] LST=3 label_f408: jp $f3ab ;[f408] label_f40b: @@ -383,7 +383,7 @@ label_f40b: label_f40c: in a,($82) ;[f40c] - bit 7,a ;[f40e] + bit 7,a ;[f40e] Read "C7" register, printer port jr z,label_f432 ;[f410] ld hl,($f471) ;[f412] dec hl ;[f415] @@ -392,15 +392,15 @@ label_f40c: or l ;[f41a] jr nz,label_f3f4 ;[f41b] xor a ;[f41d] - out ($da),a ;[f41e] - call $f368 ;[f420] + out ($da),a ;[f41e] beep... + call $f368 ;[f420] CONST, check if a character is ready or a ;[f423] - jr z,label_f3f4 ;[f424] - call $f372 ;[f426] - cp $1a ;[f429] - jr nz,label_f3f4 ;[f42b] + jr z,label_f3f4 ;[f424] if no char is ready, start back + call $f372 ;[f426] CONIN, fetch char + cp $1a ;[f429] check if key is not SUB + jr nz,label_f3f4 ;[f42b] then start back xor a ;[f42d] - ld ($0003),a ;[f42e] + ld ($0003),a ;[f42e] else reset IOBYTE ret ;[f431] label_f432: @@ -410,23 +410,24 @@ label_f432: call $f6b0 ;[f438] label_f43b: call bank_switch_off ;[f43b] - call $f3c3 ;[f43e] + call $f3c3 ;[f43e] accented letters handler call bank_switch_on ;[f441] jr z,label_f44e ;[f444] push bc ;[f446] ld c,a ;[f447] - call $f44e ;[f448] + call label_f44e ;[f448] print out this char pop bc ;[f44b] jr label_f43b ;[f44c] + label_f44e: - ld a,($0003) ;[f44e] - and $c0 ;[f451] + ld a,($0003) ;[f44e] read IOBYTE + and $c0 ;[f451] take LST cp $c0 ;[f453] - jp z,$f3c7 ;[f455] + jp z,$f3c7 ;[f455] if LST=3, jump ld a,c ;[f458] - out ($80),a ;[f459] - out ($d0),a ;[f45b] - out ($d2),a ;[f45d] + out ($80),a ;[f459] output the char on PA (parallel port bits) + out ($d0),a ;[f45b] printer handshake set + out ($d2),a ;[f45d] printer handshake reset ret ;[f45f] ld a,$01 ;[f460] diff --git a/md5sum.txt b/md5sum.txt new file mode 100644 index 0000000..24ce6f9 --- /dev/null +++ b/md5sum.txt @@ -0,0 +1,2 @@ +9a9b24ada6f7a7c7fac043b2c63fb5f8 build/cpm_bios.bin +bd40b337ba7a9b538407c2d6ac188e7a build/cpm_loader.bin diff --git a/script/docker b/script/docker new file mode 100755 index 0000000..450caea --- /dev/null +++ b/script/docker @@ -0,0 +1,16 @@ +#!/bin/bash + +# script/docker +# +# Run command in development container + +set -euo pipefail + +cd "$(dirname "$0")/.." + +docker run --rm -ti -u root \ + -v "$(pwd)":/home/builder/workspace \ + -v /tmp:/tmp \ + -w /home/builder/workspace \ + giomba/ceda-rom-disassembly:1 \ + "$@"