summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorH. Peter Anvin <hpa@zytor.com>2021-01-24 21:26:33 -0800
committerH. Peter Anvin <hpa@zytor.com>2021-01-24 21:26:33 -0800
commit0fbaafa97f224c97cb78ec274ef36d0490b45881 (patch)
tree1e392633c8342c759bc772a7029939d6963cf5e4
parentd427583b4b40c1e37fb2c5adfbe45d7036b2eebf (diff)
downloadabc80-1m-sram-0fbaafa97f224c97cb78ec274ef36d0490b45881.tar.gz
abc80-1m-sram-0fbaafa97f224c97cb78ec274ef36d0490b45881.tar.xz
abc80-1m-sram-0fbaafa97f224c97cb78ec274ef36d0490b45881.zip
sw: check in romboot.asm
-rw-r--r--sw/romboot.asm346
1 files changed, 346 insertions, 0 deletions
diff --git a/sw/romboot.asm b/sw/romboot.asm
new file mode 100644
index 0000000..869a9ab
--- /dev/null
+++ b/sw/romboot.asm
@@ -0,0 +1,346 @@
+;;; -*- asm -*-
+;;;
+;;; ROM (flash) loader for the MEG80 card. This is
+;;; designed to be a boot loader which will not need to be
+;;; frequently updated, to avoid the risk of flash failure.
+;;;
+;;; It supports up to 16 bootable configurations; these set up memory maps
+;;; 0-3 in RAM, plus copies map 0 to map 8 as a reference. To let the OS
+;;; know, enter the OS with map 8 active.
+;;;
+;;; These configurations can be selected by holding down the keys É-O
+;;; during reset (hold reset, hold down key, release reset, release key.)
+;;;
+;;; In case of flash failure, up to 10 backup configurations can
+;;; be held in ROM maps 17-26; these are selected by holding down
+;;; the keys 0-9.
+;;;
+;;; Currently (see rommap.pl):
+;;; 0 - map 17 - 20-22K, 28.5-29K, 32-64K SRAM, rest system
+;;; 1 - map 18 - 0-32K system, 32-64K SRAM
+;;; 2 - map 19 - 0-32K, 48-64K system, 32-48K SRAM
+;;; 3 - map 20 - system 0-64K
+;;; 4 - map 21 - as 0, but 0-20K, 24-28.5K, 29-30K from flash @ 16K
+;;; 5 - map 22 - as 0, but 0-20K, 24-28.5K, 29-30K from flash @ 48K
+;;; 6 - map 23 - as 0, but 0-20K, 24-28.5K, 29-30K from flash @ 80K
+;;; 7 - map 24 - as 3, but 16-24K system
+;;; 8 - map 25 - as 4, but 16-24K system
+;;; 9 - map 26 - as 5, but 16-24K system
+;;;
+;;; The ROM map page contains the instruction "OUT (C),A" as its
+;;; last two bytes; these are critical for use as a trampoline to
+;;; atomically switch maps and wrap around to the reset vector.
+;;;
+;;; If an update of the ROM maps or the bootloader gets interrupted,
+;;; open the computer and either boot with the MEG80 with the flash/system
+;;; set to "system", then reflash, or remove the flash chip and rewrite
+;;; it using an external programmer.
+;;;
+;;; This code itself uses the following memory map (map 16):
+;;; 0-4K: Bootloader code (flash @ 8K)
+;;; 4-8K: Boot configurations (see rammap.pl)
+;;; 8-16K: First 8K of system memory (used to detect TKN80)
+;;; 16-32K: 16-32K of system memory (includes video RAM incl. TKN80)
+;;; 32-56K: RAM pages 0-2 (1 and 2 currently not used)
+;;; 0x9ff0-0x9fff: minimal stack
+;;; 0x9fd0-0x9fdf: temporary variables (IY points here)
+;;; 56-64K: ROM map page, ends in trampoline
+;;;
+ include "abc80.inc"
+ defc RAMMAPS = 0x8000
+ defc RAMMAPS_END = RAMMAPS + 8192
+ defc CONFIGS = 0x1000
+ defc CONFIGS_END = CONFIGS + 4096
+ defc ROMMAPS = 0xe000
+ defc TRAMPOLINE = ROMMAPS + 8192 - 2
+ defc VRAM = 0x7c00
+ defc TMPVAR = RAMMAPS + 0x1fd0 ; Park IY here
+ defc SYSBASIC = 0x2000
+ defc ROWTBL = SYSBASIC + SCRN_ROWTBL
+ defc WIDTH = SYSBASIC + SCRN_WIDTH
+ org 0
+reset:
+ ld sp,RAMMAPS_END
+ di
+ jp _start
+ defs 0x34 - (ASMPC - reset), 0xc9 ; Fill with RET instructions
+piointa:
+ defw _piointa
+piointb:
+ defw _piointb
+ defs 56 - (ASMPC - reset)
+rst56: ret
+ defs 64h - (ASMPC - reset)
+_piointa:
+_piointb:
+ reti
+_nmi:
+ retn
+_start:
+ ;; Set up port 31 correctly for now and enable NMI
+ ld bc,0x001f
+ ld a,0x90
+ out (c),a
+ ;; --- Clear boot loader signature early
+ xor a
+ ld (+(((((0x20fc) & 3) == 2) ? ROMMAPS : RAMMAPS) + ((0x20fc) & 0x1fe8) + (((0x20fc) & 0xe000) >> 13))),a
+ ;; --- Reset I/O bus
+ in a,(7)
+ ;; --- Shut up audio
+ xor a
+ out (6),a
+ ;; --- Set interrupt mode
+ ld i,a
+ im 2
+ ;; --- Initialize PIO
+ ;; Keyboard port
+ dec a ; Bit mode
+ out (57),a
+ out (57),a ; All bits output
+ ld a,piointa & 0xff ; Interrupt vector (not used here)
+ out (57),a
+ ld a,0x07 ; Interrupt control (all interrupts disabled)
+ out (57),a
+ ;; V.24/CAS port
+ ld a,0xcf ; Bit mode
+ out (59),a
+ ld a,0x87 ; Input bitmask
+ out (59),a
+ ld a,piointb & 0xff ; Interrupt vector (not used here)
+ out (59),a
+ ld a,0x07 ; Interrupt control (all interrupts disabled)
+ out (59),a
+ ld a,~32 ; Cas relay off, TxD = CTS# = 1
+ out (58),a
+ ;; Clear screen. Do this early to keep the user from getting
+ ;; nervous. Do this "the hard way" (line by line) so we don't
+ ;; have to deal with TKN80 or not just yet...
+ ld hl,ROWTBL
+ ld bc,(WIDTH)
+ ld b,0
+ ld a,24
+clearscrn:
+ ld e,(hl)
+ inc hl
+ ld d,(hl)
+ inc hl
+ push hl
+ push bc
+ ld l,e
+ ld h,d
+ inc de
+ ld (hl),' '
+ ldir
+ pop bc
+ pop hl
+ dec a
+ jr nz,clearscrn
+ ;; Initialize all RAM memory maps to default, clear all
+ ;; metadata bytes (this includes our own stack and temp
+ ;; variables... any data that needs to be carried past
+ ;; this point MUST be in registers!!
+ ld de,RAMMAPS
+clearmaps:
+ ld hl,default_map
+ ld bc,32
+ ldir
+ ld a,d
+ cp RAMMAPS_END >> 8
+ jr nz,clearmaps
+ ;; Canonicalize I/O port 31 again
+ ld a,16
+ ld (+(((((0x001f) & 3) == 2) ? ROMMAPS : RAMMAPS) + ((0x001f) & 0x1fe8) + (((0x001f) & 0xe000) >> 13))),a
+ ;; Point IY to our private variables
+ ld iy,TMPVAR
+ ;; User pressing a key?
+checkkey:
+ ld bc,0x001f
+ in a,(56)
+ sub '+' + 128
+ jr c,default_config
+ jp z,show_configs
+ sub '0' - '+'
+ jr c,default_config
+ cp 15
+ jp z,show_configs
+ jr nc,ram_config
+ ;; ROM backup configuration
+ add 17
+ jp TRAMPOLINE ; Nothing more to do...
+default_config:
+ ld a,1 ; Configuration A
+ram_config:
+ dec a ; A=0, B=1, ...
+ and 15
+ add CONFIGS >> 8 ; 256 bytes/configuration
+ ld d,a
+ ;; Validate configuration (in D:0)
+ call check_config
+ jr nz,safe_config
+go_ram_config:
+ ;; Point DE to map data
+ ld e,112 ; Offset to the memory maps inside config
+ ;; Create maps 0 and 8
+ ld hl,RAMMAPS
+ ld b,8 ; 8 vpages
+vpage0_loop:
+ push hl
+ push bc
+ ld b,16 ; 16 subpages/page
+subpage0_loop:
+ ld a,(de)
+ ld (hl),a ; Map 0
+ inc h
+ ld (hl),a ; Map 8
+ inc h
+ inc de
+ djnz subpage0_loop
+ pop bc
+ pop hl
+ inc l
+ djnz vpage0_loop
+ ;; Detect TKN80 and set up metadata for it
+ ;; Set up default
+ ld a,(WIDTH)
+ ld (+(((((0x04bc) & 3) == 2) ? ROMMAPS : RAMMAPS) + ((0x04bc) & 0x1fe8) + (((0x04bc) & 0xe000) >> 13))),a
+ cp 40
+ ld c,3
+ jr z,is40
+ inc c
+is40:
+ in a,(4) ; Switch to 80 chars if available
+ ld a,(ROWTBL+1)
+ ld (+(((((0x5bc) & 3) == 2) ? ROMMAPS : RAMMAPS) + ((0x5bc) & 0x1fe8) + (((0x5bc) & 0xe000) >> 13))),a
+ in a,(c) ; Restore screen mode at boot
+ ;; XXX: Mangle map for TKN80
+ ;; Map 1: map 0 except flash -> RAM
+ ld e,32 ; Map 1
+ ld l,0
+ ld b,8 ; 8 vpages
+vpage1_loop:
+ push bc
+ ld b,16 ; 16 subpages
+ ld h,RAMMAPS >> 8
+subpage1_loop:
+ ld d,h
+ ld a,(hl)
+ ld (de),a
+ sub 0x80
+ cp 0x40
+ jr nc,not_flash
+ ld a,l
+ inc a
+ ld (de),a ; Replace with RAM page
+not_flash:
+ inc h
+ inc h
+ djnz subpage1_loop
+ pop bc
+ djnz vpage1_loop
+ ;; Tell the OS that we used a boot loader
+ ld a,(+(((((0x20fe) & 3) == 2) ? ROMMAPS : RAMMAPS) + ((0x20fe) & 0x1fe8) + (((0x20fe) & 0xe000) >> 13)))
+ neg
+ ld (+(((((0x20fc) & 3) == 2) ? ROMMAPS : RAMMAPS) + ((0x20fc) & 0x1fe8) + (((0x20fc) & 0xe000) >> 13))),a
+ ;; Done! Invoke trampoline to jump to map 8
+ ld a,8
+launch:
+ ld bc,0x001f
+ jp TRAMPOLINE
+safe_config:
+ ld a,17
+ jr launch
+ ;; Check if a specific configuration pointed to by D:0 is valid
+ ;; Clobbers E
+check_config:
+ push bc
+ push hl
+ ld hl,magic
+ ld b,4
+cc_loop:
+ ld e,(hl)
+ inc hl
+ ld a,(de)
+ cp (hl)
+ jr nz,cc_bad
+ inc hl
+ djnz cc_loop
+cc_bad:
+ pop hl
+ pop bc
+ ret
+ ;; Display a list of configurations
+show_configs:
+ ld ix,ROWTBL+3*2
+ ld d,CONFIGS >> 8
+ ld b,16
+sc_loop:
+ call check_config
+ jr nz,sc_bad_config
+ ;; Note: ABC80 screen lines have the very nice property
+ ;; that they never cross 256-byte boundaries, so INC L
+ ;; works fine...
+ ld l,(ix+0)
+ ld h,(ix+1)
+ inc l
+ inc l
+ ld (hl),'('
+ inc l
+ ld a,'A'+16
+ sub b
+ ld (hl),a
+ inc l
+ ld (hl),')'
+ inc l
+ inc l
+ ex de,hl
+ push bc
+ ld bc,32
+ ld l,b ; point to name string at offset 0
+ ldir
+ pop bc
+ ex de,hl
+ inc ix
+ inc ix
+sc_bad_config:
+ inc d
+ djnz sc_loop
+ ld de,(ROWTBL+23*2)
+ ld a,e
+ add 6
+ ld e,a
+ ld hl,sos_msg
+ ld bc,sos_msg_len
+ ldir
+ ;; Keyboard entry, but unlike above wait for a valid key
+sc_getkey:
+ in a,(56)
+ and a
+ sub 128+'0'
+ jr c,sc_getkey
+ cp 15
+ jr c,sc_romconfig
+ or 0x20 ; Ignore case
+ sub 0x31 ; A->0 etc
+ cp 16
+ jr nc,sc_getkey
+ add CONFIGS >> 8
+ ld d,a
+ call check_config
+ jp z,go_ram_config
+ jr sc_getkey
+sc_romconfig:
+ add 17
+ jp TRAMPOLINE
+ ;; Emergency help
+sos_msg:
+ defm "0-9 rescue configurations"
+ defc sos_msg_len=(ASMPC - sos_msg)
+ ;; Flash entry magic numbers
+magic:
+ defb 32, 0x89, 33, 0x0d, 254, 0xed, 255, 0x79
+ ;; Default map entry: 0-32K system, 32-64K SRAM
+default_map:
+ defb 0xc0, 0xc1, 0xc2, 0xc3, 0x05, 0x06, 0x07, 0x08
+ defs 24, 0
+_fill:
+ defs 4096 - (ASMPC - reset), 0xff