;;; -*- asm -*- ; ;;; Simple option ROM for printer for ABC80; this is not authentic, but ;;; uses a hardware interface implementable with an FT232H USB module. ;;; It is (hopefully) at least similar to an ABC80 printer interface. ; ;;; - ABC-bus select code 60 decimal; ;;; OUT 0 - (OUT) output data ;;; IN 0 - (INP) input data ;;; IN 1 - (STAT) bit 0 - Rx data available ;;; bit 1 - Tx space available ;;; bit 2 - USB suspended (not ready) ;;; bit 3 - USB configured (host ready) ;;; OUT 2 - (C1) flush Tx buffer immediately ;;; OUT 3 - (C2) discard all input - only supported in some hw versions ;;; ;;; ;;; FF is used as an escape character for the file I/O and console ;;; interfaces (FF A0..BF for file, FF C0 for console) ;;; ;;; FF FE is used to represent an FF character in the printer stream, ;;; and FF FD is send on CLOSE (end of job). ;;; For other escape sequences, see print.c. ;;; ;;; To auto-initialize this, we need a modified DOS that looks for ;;; additional ROM entry points (ABC800 has this, but not ABC80.) .altmacro #include "z80.inc" #include "abc80.inc" #include "auxram.inc" #include "ufddos.inc" defc selcode=60 ; ABC-bus select code defc LARGE=(ROMSIZE >= 2048) ;; Maximum number of remote volumes defc MAX_VOLS = 32 ;; Console device, probably doesn't fit anymore defc console_dev = LARGE ;; Support strict text file access defc pra_dev = 0 ;; ABC800-style CON: and NUL: devices defc connul = LARGE ;; Quicklib routine defc quicklib = 1 ;; Return real error codes or always zero? defc return_real_errors = 0 ; Match UFD-DOS 80 ;; PR: using raw protocol (not a chardev volume?) defc raw_pr = 0 ;; XDx: device entries (secondary hard disk, not enough space in DOS) defc xd_dev = 1 ;; CMD routine if in IEC area? defc have_cmd = LARGE && (ROMSTART == 0x7000) if !have_cmd defc c_cmd = ERR_8 defc e_cmd = ERR_8 defc e_iec = ERR_8 endif ;; Device timeout defc timeout = 100 ; 100/50 Hz = 2 s .globl _org _org = ROMSTART .section .text .globl _start _start: ;; If in the IEC area, add the IEC area entry points ;; IEC: jump table follows; simply treat it as PR: if ROMSTART == 0x7000 jp c_cmd jp e_cmd jp e_iec endif if raw_pr ; jp table for PR: (if in printer area, should be first) pr_jptable: jp trivial ; OPEN jp trivial ; PREPARE jp pr_close ; CLOSE jp notforme ; INPUT jp pr_print ; PRINT jp notforme ; RDBLK jp notforme ; WRBLK jp notforme ; DELETE jp notforme ; RENAME endif prb_jptable: jp prb_open ; OPEN jp prb_prepare ; PREPARE jp prb_close ; CLOSE jp prb_input ; INPUT - might be generally useful jp prb_print ; PRINT jp prb_rdblk ; RDBLK jp prb_wrblk ; WRBLK ; Supporting DELETE and RENAME requires fixes to the DOS ROM; ; the DOS ROM contains the BASIC interpreter for these and ; it assumes it only applies to disk files. jp prx_delete ; DELETE jp prx_rename ; RENAME ;; Fixed entry points, reserved for future use fixfunc: .org 0x4a, 0xff ;; Magic number that this entry point is valid dec a ;; Init entry point, same offset as in DOS jp start if pra_dev ; jp table for PRA: pra_jptable: jp pra_open ; OPEN jp pra_prepare ; PREPARE jp pra_close ; CLOSE jp pra_input ; INPUT jp pra_print ; PRINT jp notforme ; RDBLK jp notforme ; WRBLK ; Supporting DELETE and RENAME requires fixes to the DOS ROM; ; the DOS ROM contains the BASIC interpreter for these and ; it assumes it only applies to disk files. jp prx_delete ; DELETE jp prx_rename ; RENAME endif if raw_pr pr_print: call select ld a,0F3h ; Printer data call send_esc pr_print_loop: ld a,b or c ld a,0EFh jr z,done_esc ld a,(hl) call send_byte inc a jr nz,pr_not_ff ld a,0FEh call send_byte pr_not_ff: dec bc inc hl jr pr_print_loop pr_close: call select ld a,0FDh jr done_esc endif ;; Select the current device, and save the stack pointer ;; to allow for an error unwind. This should be called from the ;; topmost stack so that a RET on error returns to BASIC; ;; if called in a nested context that context will be returned ;; to on error. After an error the carry flag is set and only ;; A is defined. select: ld (errsp),sp push af ld a,(SELECT) ; Old select code ld (ram_select),a ; Save old select code ld a,selcode ; Select code ld (SELECT),a ; Is this correct? out (1),a pop af ret done_esc: call send_esc done_ok: xor a done: ld sp,(errsp) ; Restore stack inc sp ; Drop return address from select inc sp push af ld a,(ram_select) ld (SELECT),a out (1),a pop af rlca ; CF <- bit 7 if return_real_errors srl a ; Strip bit 7 from result but is in CF else ld a,0 ; This is what UFD-DOS 80 does?! endif ret if pra_dev pra_open: ld a,0xA0 jr prx_open pra_prepare: ld a,0xA2 jr prx_open endif prb_prepare: ld a,0xA3 jr prb_open_prepare prb_open: ld a,0xA1 prb_open_prepare: call prb_setup_buf ;; Fall through... prx_open: ;; ABC80-BASIC expects DE to be preserved across this routine, ;; otherwise .BAC -> .BAS searching does not work. if pra_dev ; Handled by prb_setup_buf ld (ix+7),132 ; Line length, seems to be standard? endif call select push af ; Save command code call send_cmd ; Returns with CF = 0 ld l,(ix+3) ; Get volume name ld h,(ix+4) ld bc,3 sbc hl,bc ld (ix+6),b ; 0 call send_buf ;; HL <- filename. Do not use EX DE,HL here: if send_buf ;; aborts we have to have DE intact on return to BASIC. ld l,e ld h,d ld c,11 ; B = 0 already call send_buf call recv_reply pop hl ; H = command if quicklib and (iy+14) ; Bit 0 = command mode add a ; Bit 0 -> bit 1 and h ; Check for PREPARE (bit 1) ;; bit 1 in A is now set if quicklib endif ;; Mark buffer busy if successful, any value other than 0xff ;; works, so just use zero... if pra_dev bit 0,h ; PRA -> no buffer, no quicklib jr z,nohazbuf endif call prb_dosfd ld (hl),b ; B = 0 on exit here nohazbuf: if !quicklib jr done else ;; A is as set up above... and 2 jr z,done ; Not quicklib (A = 0 here) ;; -------- Show library listing on screen -------- prx_quicklib: ;; Restore the original select code in memory. It isn't ;; necessary to send the select code to the bus, because ;; the subsequent I/O operations will do that for us. ld a,(ram_select) ; Restore original select code ld (SELECT),a call crlf ld (ix+6),22 ; 22 lines before wait ql_getline: call S_CHECKCTRLC jr nz,ql_done ld hl,PCODE_BUF push hl ld bc,PCODE_SIZE call IX_INPUT pop hl jr c,ql_done call S_CUR_MAX_X ld bc,+(12 << 8) + 13 add c ; Space needed sub e jr c,ql_nocrlf call crlf dec (ix+6) jr nz,ql_nocrlf push ix call CON_GET pop ix ld (ix+6),1 ; = one line only cp c jr z,ql_nocrlf ld (ix+6),22 ql_nocrlf: push hl ql_find_comma: ld a,(hl) cp '.' ; Lowest valid filename character jr c,ql_found_comma ; Comma, CR, or other non-filename character inc hl djnz ql_find_comma ql_found_comma: inc b ql_pad_space: ld (hl),' ' inc hl djnz ql_pad_space ql_cleaned_comma: pop hl ; BC = 13 here call CON_PRINT jr ql_getline ql_done: call S_CLOSEFILE jp S_CMD ; Forcible return to command loop - ; abort LIST command crlf: push bc call CON_CRLF pop bc ret endif ; quicklib prx_rename: call select ld a,0xAA call send_cmd ex de,hl ; HL -> new filename ld bc,11 call send_buf jr prx_reply_done prb_close: call select xor a cp (ix+14) jr z,pbc_noflush call prb_getbufaddr ld b,a ld a,(ix+10) ; End data sub l ; Skip non-data at start of buffer ld c,a call output_common pbc_noflush: call prb_dosfd ld (hl),255 ; Buffer now free ;; Returns with B=0 ld (ix+14),b if pra_dev pra_close: ld (ix+5),0 else ld (ix+5),b endif ld a,0xA7 prx_simple_command: ; Just command and response, no data call send_cmd prx_reply_done: ; Reply, then done call recv_reply done2: jp done prx_delete: ld a,0xAB call select jr prx_simple_command if pra_dev pra_print: call select call output_common jr done2 endif output_common: ld a,0xA6 prx_length_command: call send_cmd call send_word call send_buf jp recv_reply if pra_dev pra_input: call select ld a,0xA4 ; INPUT endif ;; This routine can be called to handle a command with a ;; reply + len + data response. ;; HL -> target_buf, A = command, BC = buffer size ; XXX: USB is fast enough that INIR/OTIR should be possible input_command: call send_cmd call recv_reply call recv_byte ld e,a call recv_byte ld d,a ; Now HL -> target buf; DE -> expected byte count; ; BC -> buffer size prai_loop: ld a,d or e ret z dec de ld a,b or c jr nz,prai_space ld hl,ram_dummy inc c prai_space: dec bc call recv_byte ld (hl),a inc hl jr prai_loop prbr_protoerr: ld a,128+37 jr done2 prb_rdblk: call select prb_rdblk_nosel: ld a,0xA5 call send_cmd ld b,253 ld a,b call send_byte xor a call send_byte call recv_reply call recv_byte cp b jr nz,prbr_protoerr call recv_byte and a jr nz,prbr_protoerr call prb_getbufaddr push hl prbr_recv: call recv_byte ld (hl),a inc hl djnz prbr_recv pop hl ; HL -> buf address jp done_ok ;; This is a slightly modified version of the BLK_INPUT routine ;; in the BASIC ROM. It has the following modifications: ;; 1. Accept or line endings as well as just ;; 2. Handle a block without an terminator ;; 3. Handle a file without terminator ;; These changes allow a text file in modern Unix/DOS format ;; to be parsed, as long as it does not have embedded ;; characters. It would be possible to heuristically handle ;; by looking for or ; the first block ought to have ;; at least one if "modern", or if ABC. ;; ;; This is intended to be a general routine, usable for other ;; devices than these. prb_input: ex de,hl ; DE -> destination pointer ld (ix+13),1 ; Inhibit output ld l,(ix+10) ; HL -> pointer into the buffer ld h,(ix+11) pbi_nextchar: xor a cp c jr z,pbi_overlong ; Output buffer overflow? call pbi_peekchar cp 9 ; TAB = compressed spaces jr z,pbi_tab cp 10 ; LF = convert to CR jr z,pbi_lf and a jr z,pbi_eof ; NUL = end of file jp m,pbi_badchar ; >= 128 -> error pbi_emit: ldi ; (DE) <- (HL), DE++, HL++, BC-- cp 13 ; CR = end of line jr nz,pbi_nextchar ;; For a CR character, look to see if the next one if LF, ;; if so eat the LF rather than having it converted to CR next time. ;; Only do this exactly once, so = 2 lines. call pbi_peekchar cp 10 jr nz,pbi_done inc hl ; Skip exactly one LF character pbi_done: xor a pbi_return: ld (ix+10),l ld (ix+11),h ret pbi_eof: pbi_err: scf jr pbi_return pbi_overlong: ld a,128 + 20 ; Line too long jr pbi_err pbi_badchar: ld a,128 + 58 ; Bad character jr pbi_err pbi_lf: ; Convert to CR and EOL, but don't eat LF ld (hl),13 ldi jr pbi_done pbi_tab: inc l ; Next byte; note that 3 = ETX is valid jr z,pbi_nextchar ; at end of block -> ignore ld b,(hl) inc l ld a,c sub b jr c,pbi_overlong ld c,a ld a,' ' pbi_tab_expand: ld (de),a inc de djnz pbi_tab_expand jr pbi_nextchar ;; Get the next character, but don't advance HL beyond ;; it. However, handle ETX and buffer overrun here. pbi_peekchar: xor a cp l ; 256-byte wrap: assume end of block jr z,pbi_newblock ld a,(hl) cp 3 ret nz pbi_newblock: call pbi_eofbuf ; Just in case IX_BLKRD fails silently... push bc push de call IX_BLKRD pop de pop bc ;; HL points to start of buffer here (we hope) jr nc,pbi_peekchar ;; On error, return EOF and also set the first byte in the buffer ;; to EOF in case input gets called again. ;; Fall through... pbi_eofbuf: call prb_getbufaddr xor a ld (hl),a ret ; Write a binary output block. Similar to ; pra_print, but with a few different buffer ; management bits. prb_wrblk: call select call prb_getbufaddr ld bc,253 call output_common call prb_empty_buf done3: jp done ;; Blocking PRINT without converting to ABC-DOS text format. ;; This simply stuffs a buffer full of the PRINT contents; ;; it also assumes the buffer is full when there is a 256-byte ;; address wraparound as (ix+13) is not expected to reflect the ;; buffer length without space for ETX... and is set to 1 on ;; read (what?!) prb_print: ld a,c or b ret z ; Nothing to do... ld e,(ix+10) ld d,(ix+11) pbp_output: xor a sub e jr z,pbp_nextbuf ; No space left in buffer push bc inc b ;; A now has the number of free bytes in the buffer ;; If BC > A need to use the rest of the buffer djnz pbp_limitlen cp c jr c,pbp_limitlen ld a,c pbp_limitlen: ld c,a xor a ; Assume all good, clear carry ld b,a ex (sp),hl sbc hl,bc ; Decrement bytes left to write ex (sp),hl ldir ld (ix+10),e ld (ix+11),d ld (ix+14),1 ; Buffer dirty pop bc ret z ; Z still set from SBC HL,BC pbp_nextbuf: push hl push bc call IX_BLKWR pop bc pop hl jr nc,pbp_output ret ; Error pbp_allbuf: sub e jr pbp_limitlen ;; Get the address of the DOS file description corresponding ;; to the allocated buffer. ;; Returns with B = 0, C = DOSBUF*16, HL = DOS file description prb_dosfd: ld b,0 ld c,(ix+12) ld hl,0xFD41 add hl,bc ret ;; Set up a BUF for PRB: (open/prepare) ;; This uses routines from UFD-DOS due to space prb_setup_buf: push af push bc push hl call DOSALLOCBUF ; Allocate buffer call DOSBUFINIT ; Initialize buffer pointers pop hl pop bc pop af ret ;; This returns with DE -> buffer start. It seems ABC80-BASIC ;; requires this to work... prb_empty_buf: ; Returns with A=0, HL->buf push af ex de,hl ld a,(ix+12) call DOSBUFREINIT ; Returns with HL -> buffer ex de,hl pop af ret ;;; Load the address of the start of the current buffer into HL prb_getbufaddr: ld l,3 ld h,(ix+9) ret ;; Send a command header, A = command ;; Returns with CF = 0 send_cmd: push bc ld (ram_cmd),a call send_esc ld a,(ram_serial) call send_byte push ix pop bc call send_word pop bc ret ;; Send a word in BC, preserves AF send_word: push af ld a,c call send_byte ld a,b call send_byte pop af ret ;; Send FF + byte; reset running checksum ;; Returns with CF = 0! send_esc: push af ld a,0FFh call send_byte ld (ram_csum),a pop af ;; Fall through ;; Send a single byte and add to running checksum ;; Returns with CF = 0! send_byte: push hl ld l,a ld a,(ram_csum) add l ld (ram_csum),a in a,(1) and 2 ; TX space available call z,wait_for_tx ld a,l out (0),a pop hl ret ;; Send a buffer HL->data BC=count ;; On return HL advanced, BC=0, A clobbered ;; ; XXX: USB is fast enough that INIR/OTIR should be possible send_buf: ld a,b or c ret z ld a,(hl) call send_byte dec bc inc hl jr send_buf ;; Receive a byte. Return with C flag on timeout. recv_byte: ;; Optimistically... in a,(1) and 0Dh cp 09h call nz,wait_for_rx in a,(0) ; Data immediately available ret ; C=0 already ;; This routine waits until the status bit given in A is zero; ;; if the the high bits of the status are anything other than ;; xxxx10xx (configured, not suspended) then immediately issue ;; device not ready. wait_for_tx: push hl ld hl,0x0A0E jr wait_for wait_for_rx: push hl ld hl,0x090D ; Mask, expected value wait_for: push bc ld b,timeout wf_ctr: ld a,(CLOCK) ld c,a wf_loop: in a,(1) and l cp h jr nz,wf_nope pop bc pop hl ret wf_nope: and 0x0C ; Invalid status bits? cp 0x08 jr nz,wf_error ; Not connected ld a,(CLOCK) cp c jr z,wf_loop djnz wf_ctr wf_error: ld a,128+42 ; Device not ready done4: jp done send_endframe: ld a,0EFh ; End of frame call send_byte ld a,(ram_csum) neg ; Total checksum = 0 call send_byte out (2),a ; Immediately transmit any pending output ret ;; Send end of frame, and receive a reply header. ;; If the reply is an error, terminate command and return ;; the error code to BASIC (or other caller) by calling done. recv_reply: push bc call send_endframe ld bc,(ram_cmd) rr_loop: call recv_byte rr_isff: inc a ; FF? jr nz,rr_loop call recv_byte cp c ; Command jr nz,rr_isff call recv_byte cp b ; Serial jr nz,rr_isff call recv_byte ; Error code (if any) ;; Got this far, bump sequence number inc b ld (ram_cmd),bc pop bc and a ret p ; Bit 7 = 0 -> no error jr done4 rr_timeout: dec b ; Drop the sequence number to allow resend jr wf_error ;;; ;;; Discard any (stale) input, if any. Terminate immediately if suspended, ;;; not configured, or not present. Clobbers A. ;;; flush_rx: in a,(1) and 0Dh sub 09h ret nz out (3),a ; Fast flush if supported by the hardware in a,(0) ; Consume byte jr flush_rx if console_dev prc_jptable: jp trivial ; OPEN jp trivial ; PREPARE jp trivial ; CLOSE jp notforme ; INPUT jp prc_print ; PRINT jp notforme ; RDBLK jp notforme ; WRBLK jp notforme ; DELETE jp notforme ; RENAME prc_print: call select ld a,0xFF call send_byte ld a,0xc0 call send_byte prc_print_loop: ld a,b or c jr z,send_byte_done ; A=00 here ld a,(hl) and a jr z,prc_skip call send_byte prc_skip: dec bc inc hl jr prc_print_loop send_byte_done: call send_byte jp done_ok endif ; console_dev start: if have_cmd ld hl,new_basic_jumps ld bc,old_basic_jumps - new_basic_jumps ld a,(65h) ; First byte different between old and new and a jr z,is_new_basic add hl,bc is_new_basic: ld de,basic_jumps ldir endif call init ; Unwind to here on error or return ld hl,__end + INIT_OFFS ; Where to search next ret init: call select ld hl,-256 add hl,sp ld sp,hl push hl ld a,0xA9 ; CLOSE ALL NO REPLY call send_cmd call recv_reply ld a,0xBA ; LIST VOLUMES pop hl push hl ld bc,4*MAX_VOLS+1 ; B=0 call input_command pop hl ;; Create device list entries ;; HL -> CRAD here, A = 0 push hl ld bc,4*MAX_VOLS add hl,bc ld (hl),b ; Make sure it is null-terminated on overflow pop hl ld de,voldevs ;; Are we running with auxram on the stack? bit 7,d jr z,1f ld (STACK),de ; Move down the user stack 1: mk_vols: ld a,(hl) and a jr z,link_volumes ; Done inc hl push hl ld hl,7 add hl,de ex de,hl ld (hl),e inc hl ld (hl),d inc hl ex de,hl pop hl ld bc,3 ; Copy volume name ldir if pra_dev ;; This requires pra_jptable and prb_jptable have the same upper bytes dec a ; Z = 1 if A = 1 = text/PRA ld a,pra_jptable & 0xff jr z,have_voltype endif ld a,prb_jptable & 0xff have_voltype: ld (de),a inc de ld a,prb_jptable >> 8 ld (de),a inc de jr mk_vols link_volumes: ;; A = 0 here ld hl,-7 add hl,de ld de,(DEVLIST) ld (hl),e inc hl ld (hl),d ld hl,device_list ld (DEVLIST),hl jp done notforme: ld a,128+52 ; Ej till denna enhet scf ret if connul con_open: ;; Be nice and initialize the position fields, even though ;; we aren't really able to keep them updated... call S_CUR_MAX_X ld (ix+6),a ld (ix+7),e ;; Fall through endif trivial: ; Trivially successful call xor a ; A <- 0 carry <- 0 ret if connul alwayseof: ; End of file or file not found xor a scf ret ;;; ;;; Simple CON and NUL devices for ABC800 compatibility ;;; con_jptable: jp con_open ; OPEN jp con_open ; PREPARE jp trivial ; CLOSE jp CON_INPUT ; INPUT jp CON_PRINT ; PRINT jp notforme ; RDBLK jp notforme ; WRBLK jp notforme ; DELETE jp notforme ; RENAME nul_jptable: jp trivial ; OPEN jp trivial ; PREPARE jp trivial ; CLOSE jp alwayseof ; INPUT jp trivial ; PRINT jp notforme ; RDBLK jp notforme ; WRBLK jp notforme ; DELETE jp notforme ; RENAME endif ;;; ;;; Support for CMD (via the IEC area) ;;; ;;; CMD [#device,] expr [,expr] ... ;;; ;;; Similar to PRINT except that each argument is terminated by ;;; a null byte before sending ;;; if have_cmd defc CMD_BUF = LINE_BUF defc CMD_BUF_LEN = LINE_SIZE ;; Need 16 bytes of overflow space for numbers defc CMD_BUF_END = CMD_BUF + CMD_BUF_LEN - 16 defc e_iec = ERR_8 ; Not implemented e_cmd: ld l,0 ; No file number ld a,(de) ; Next byte code cp '#' jr nz,e_cmd_find_file inc de rst 56 ; Compute expression e_cmd_find_file: ld (iy+13),l ld ix,0 ; No file map ld a,l and a jr z,e_cmd_start ; Device 0 ;; L = file number to find ld ix,FILELIST e_cmd_find_file_next: ld c,(ix+0) ; Link ld b,(ix+1) ld a,c or b jp z,ERR_32 ; File not open push bc pop ix ld a,(ix+2) ; File number cp l jr nz,e_cmd_find_file_next ;; IX -> file map e_cmd_start: push ix ld hl,CMD_BUF ld (e_cmd_buf_ptr),hl ld (errsp),sp ; SAve stack pointer for reset e_cmd_start_expr: ld a,(de) inc de cp ',' jr z,e_cmd_start_expr cp ';' jr z,e_cmd_start_expr dec de cp 192 ; Byte code for expression jr c,e_cmd_done ; Not an expression? rst 56 ; Compute expression push de ; Save P-code pointer ld de,(e_cmd_buf_ptr) dec b ; Integer? jr nz,e_cmd_not_integer e_cmd_integer: call INT_TO_STR jr e_cmd_finish_num e_cmd_not_integer: dec b ; String? jr z,e_cmd_string e_cmd_float: ;; Floating point ld hl,2 add hl,sp ; -> exponent on stack call FLOAT_TO_STR e_cmd_finish_num: call e_cmd_check_overflow e_cmd_finish: ld (de),a inc de ld (e_cmd_buf_ptr),de pop de ; P-code pointer ld sp,(errsp) ; Reset stack jr e_cmd_start_expr e_cmd_string: pop hl ; P-code pointer pop bc ; Stack adjustment (not used) pop bc ; String address ex (sp),hl ; String length in HL, P-code pointer on stack push bc ; String address push hl ; String length push de ; Output pointer add hl,de ; End of string ex de,hl call e_cmd_check_overflow pop de ; Output pointer pop bc ; String length pop hl ; String pointer ldir jr e_cmd_finish e_cmd_done: ;; Output generated, now send it pop ix ; Restore IX map pointer push de ; P-code pointer ld hl,(e_cmd_buf_ptr) ld de,CMD_BUF xor a sbc hl,de ex de,hl ld c,e ld b,d pop de call select ld a,0BBh ; GENERIC COMMAND call prx_length_command jp done e_cmd_check_overflow: ;; DE -> end of string data push hl ld hl,CMD_BUF_END-1 ; -1 for terminal null xor a sbc hl,de pop hl ret nc ; All good e_cmd_space_overflow: rst 10h defb 128 + 20 ; "För lång rad" endif ; have_cmd _device_link = voldevs .macro device name:req, jptbl:req defw _device_link _device_link = . - 2 defm \name defw \jptbl .endm if xd_dev xd_device: device "XD0", DOSJPTABLE defb 0x14 device "XD1", DOSJPTABLE defb 0x15 device "XD2", DOSJPTABLE defb 0x16 device "XD3", DOSJPTABLE endif if console_dev prc_device: device "PRC", prc_jptable endif if connul con_device: device "CON", con_jptable nul_device: device "NUL", nul_jptable endif if raw_pr pr_device: device "PR ", pr_jptable endif ;; The value to put in the BASIC device link pointer device_list = _device_link ;; Jump table for BASIC functions that are inconsistent new_basic_jumps: ; Checksum 9913, 10042 if have_cmd jp 3392h ; Check space on stack jp 1853h ; Integer to string jp 1675h ; Float to string jp 2152h ; Compile PRINT endif old_basic_jumps: ; Checksum 11273 if have_cmd jp 339Bh ; Check space on stack jp 1855h ; Integer to string jp 1679h ; Float to string jp 2154h ; Compile PRINT endif ;; Check for overflow and pad to desired ROM size .org ROMSIZE, 0xff __end: ;; ;; In RAM; this is assuming external SRAM or equivalent at 20-22K. ;; The top 64 bytes are used by UFD-DOS. ;; defc ram_end = AUXRAM_PRINTNET_END defc ram_start = AUXRAM_PRINTNET_BASE .globl _bss _bss = AUXRAM_PRINTNET_BASE section .bss __bss: voldevs: defs 7*MAX_VOLS ; Up to 32 volume device entries errsp: defs 2 ; SP rollback on error return e_cmd_buf_ptr: defs 2 ; Current data buffer pointer ram_select: defs 1 ; Previous select code ram_csum: defs 1 ; Running output checksum ram_cmd: defs 1 ; Latest sent command ram_serial: defs 1 ; Latest serial number (must be immediately after ram_cmd) ram_dummy: defs 1 ; Scratch byte basic_jumps: if have_cmd CHECK_STACK_SPACE: defs 3 ; Check space on stack INT_TO_STR: defs 3 ; Integer to string FLOAT_TO_STR: defs 3 ; Float to string c_cmd: ; Compile CMD = same as PRINT C_PRINT: defs 3 ; Compile PRINT endif ;;; Check for overflow .org AUXRAM_PRINTNET_SIZE __end_bss: