; FLASH GORDON driver ; Copyright 2012, Adrien Destugues ; This program is distributed under the terms of the MIT licence GLOBAL _start ; Friendly firmware functions getc EQU 0xBB06 putc EQU 0xBB5A screenmem EQU 0xBC08 fopen EQU 0xBC77 fread EQU 0xBC83 fclose EQU 0xBC7A ; FIXME the plan is to have the same file behave both as a ROM and as an ; executable, so it can bootstrap itself into the megaflash. So we should start ; with a standard ROM header. RSXs to expose are : ; |BURN,"file",n (load file to &4000, then copy it to ROM n) ; |HIDE,n (write a FF as first byte in ROM to hide it from firmware) ; |HIDE (hides all ROMs) ; |SHOW,n (restore ROM n to visible state ; |SHOW (shows all ROMs) ; |KILL,n (erases ROM n) ; This is the entry point when running as a standalone file _start ; Check the parameter count CP 2 JP NZ,params_error ; Load the file to burn ; Get call parameter LD L,(IX+2) LD H,(IX+3) ; Resolve BASIC string descriptor LD B,(HL) ; length INC HL LD E,(HL) INC HL LD D,(HL) EX DE,HL LD DE,0x8000 CALL fopen RET NC ; file not found (TODO print error message before leaving) LD HL,0x2000 CALL fread RET NC CALL fclose ; Ask the user to allow us to write to the flash memory LD HL,MSG_WRITE_START CALL puts CALL getc ; From there on, writing is enabled. This is ok as long as we don't ; connect a writable ROM ! When we do, we have to make sure NOTHING ; is read/written from 8000-FFFF because the MegaFlash hardware ; doesn't decode A14 and will answer for the whole 32k range. This ; means, no use of firmware jumpblocks, and the stack needs to be ; moved elsewhere DI ; Move the stack away from the $8000-$FFFF area, since any write there ; will be intercepted by the Megaflash (it does not decode A14). LD (stack),SP LD SP,0x7FFF ; Save the interrupt vector and replace it with EI/RET LD HL,(0x38) LD (inthandle),HL LD HL,0xC9FB LD (0x38),HL ; Now that we got the system out of the way, map the ROM ; (we stay in mode 1) LD BC, 0x7F85 OUT (C),C ; TODO get the ROM number to burn somehow ? ; (we can get it as a CALL parameter) CALL ERASE CALL CHECKBLANK ; Load the "erase error" message. If erase actually succeded, we will go ; to the write function which will set something else. ; DE holds the error address EX DE,HL LD HL,MSG_ERASE_ERR LD (status),HL CALL Z,WRITE ; Disconnect the Megaflash LD BC,0xDFFF OUT (C),C ; Restore the firmware in working order now that we are done LD SP,(stack) LD HL,(inthandle) LD (0x38),HL EI ; Tell firmware to draw the screen at &4000 (so we don't erase the ; ROM with the next message...) LD A,0x40 CALL screenmem ; TODO clear screen ; Print the error/success message LD HL,(status) CALL puts ; Print DE so we know where the problem was LD A,D CALL PRNHEX2 LD A,E CALL PRNHEX2 ; Now user can safely disconnect the ROM LD HL,MSG_WRITE_END CALL puts ; Wait for key CALL 0xBB06 ; Put screen back at normal address LD A,0xC0 CALL screenmem _exit ; TODO cleanly get out (and handle both calls by RUN and CALL) RET params_error LD HL,MSG_HELP CALL puts JR _exit ; Erase a 16K ROM. Input: ; A - ROM number to erase (TODO) ; Assumes we are in write mode, interrupts disabled. ERASE LD B,4 eraseloop PUSH BC LD A, 0x80 ; ERASE CALL send_command CALL prepare_command LD B,0xDF LD C,(IX+0) ; ROM number to write OUT (C),C ; Compute address near start of sector POP BC LD HL, addrmap-1 LD D,0 LD E,B ADD HL,DE LD A,(HL) LD H,A LD L,0 ; Sector erase command LD E, 0x30 LD (HL), E ; This will delay long enough - we need at least 20ms EI HALT HALT HALT HALT HALT HALT HALT DI DJNZ eraseloop RET addrmap defb 0xC0, 0xD0, 0xE0, 0xF0 ; Write a 16K ROM ; IN: A - ROM number to write (TODO) ; &2000 - Data to copy (TODO let the caller put that in IX maybe ?) ; Assumes we are in write-enabled mode (TODO) WRITE ; Ok, now that we erased the 4 sectors we needed, we can write data to them LD HL, 0xC000 LD IY, 0x2000 PROGRAM PUSH HL LD A, 0xA0 ; BYTE PROGRAM CALL send_command POP HL LD B,0xDF LD C,(IX+0) ; ROM number to write OUT (C),C LD A,(IY + 0) LD (HL),A ; Write occurs here. Need to wait 20 NOPs before next ; operation on ROM. Our code is slow enough already ! INC IY INC HL ; As INC HL doesn't update the flags, check if we overflowed yet LD A,H OR L JR NZ, PROGRAM LD HL,MSG_WRITE_OK LD (status),HL RET ; Check if the ROM was erased properly and contains all FF bytes. ; Input: upper ROM is connected ; Output: Z flag set if all ok, else HL points to error CHECKBLANK ; TODO select the right ROM LD B,0xDF LD C,(IX+0) OUT (C),C LD HL, 0xC000 .checkbyte LD A,(HL) CP 0xFF RET NZ INC HL LD A,H OR A RET Z JR .checkbyte ; Helper routines ----------------------------------------------------- ; Send a command to the ROM. The available commands are : ;ERASE equ 0x80 IDENTIFY equ 0x90 BYTEPROG equ 0xA0 RESET equ 0xF0 ; Input: Commend to send in A register send_command: ; That code is shared with sector erase which has some tricks call prepare_command ; Select ROM 1 again and write the command to address 0xEAAA DEC C OUT (C),C EX DE,HL LD (HL), A ; Select ROM 0xFF, which basically prevents future accesses to get to the ; Flash chip. DEC C DEC C OUT (C),C RET prepare_command: ; Select ROM 1 LD BC,0xDF01 LD HL,0xD555 LD DE,0xEAAA ; Select ROM 1 and write 0xAA to address 0xD555 OUT (C),C LD (HL),E ; Select ROM 2 and write 0x55 to address 0xEAAA INC C OUT (C),C EX DE,HL LD (HL), E RET ; Write message pointed by HL to screen puts LD A,(HL) INC HL CALL &BB5A OR A JR NZ,puts RET ; Print A value as hexadecimal ; AF is corrupt PRNHEX2 PUSH AF RRA RRA RRA RRA ;divise A par 8 CALL PRNHEX1 ;affiche le premier morceau POP AF ;r{cup la valeur originale pour la suit PRNHEX1 ;affiche la valeur de A sur 1 chiffre ;affiche A sur 1 chiffre en Hexa AND 0xF ;ne prend que les unit{s OR A DAA ;convertit en d{cimal ADD A,0xF0 ;ajoute 240 ADC A,0x40 ;ajoute 64+le carry (si >15) ;on a le code ascii du chiffre @ afficher JP 0xBB5A ;call-ret ; ----------------------------------------------------------------------------- ; Messages MSG_WRITE_START string "Turn write switch ON and press a key...\r\n" MSG_WRITE_END string "Turn write switch OFF and press a key...\r\n" MSG_ERASE_ERR string "Erasing memory failed at address " MSG_WRITE_OK string "Success!\r\n" MSG_HELP string "CALL &1800,\"filename.rom\",nn\r\n" ; ----------------------------------------------------------------------------- SECTION .uninit,"urw" stack DEFW 1 inthandle DEFW 1 status DEFW 1 end ; END END END END END END END END END END END END END END END END END END END ; Very basic command prompt/monitor stuff for testing ; Print prompt LD A,'?' CALL 0xBB5A ; Wait for key CALL 0xBB06 ; Parse commands CP A,'d' JP Z,DUMP CP A,'e' JP Z,ERASE CP A,'w' JP Z,WRITE ; Unhandled command LD A,'X' CALL 0xBB5A ; Start over JR _start ; Dump some bytes from beginning of ROM 5 DUMP DI ; Map the ROM in (stay in mode 1 and get system rom out) LD BC, 0x7F85 OUT (C),C LD BC,0xDF05 OUT (C),C ; Copy the ROM to RAM LD DE, 0x4000 LD BC, 0x4000 LDIR EI ; Dump the RAM (we're safe if the system maps another ROM this way) LD HL,0x4000 plop LD A,(HL) INC HL CALL PRNHEX2 DJNZ plop ; Wait for next command RET