;Oct 18, 2020 ;standalone-ROM-5.asm ;ROM monitor for video display system with keyboard (standalone system) ;Same as standalone-ROM-4.asm with menus for ROM basic ;Tiny BASIC not included in assembly language here because it is in 8080 code ;Instead, room made for putting Tiny BASIC binary at basic label ; org 0000h jp monitor_cold_start ;The subroutines for the serial port use these variables in RAM: ;Need to have stack in upper RAM, but not in area of CP/M or RAM monitor. ROM_monitor_stack: equ 0x7fff ;at top of RAM1 ;Video RAM area vram_start: equ 0x8000 vram_end: equ 0xa57f ; ;Subroutine to initialize serial port UART ;Needs to be called only once after computer comes out of reset. ;If called while port is active will cause port to fail. ;16x = 9600 baud ;call commented out in RAM monitor initialize_port: ld a,04eh ;1 stop bit, no parity, 8-bit char, 16x baud out (3),a ;write to control port ld a,037h ;enable receive and transmit out (3),a ;write to control port ret ;write_char subroutine ;calls put_char after setting registers with the character, line and position values ;Does newline and backspace ;ASCII character to write in A reg write_char: cp 0x0d ;carriage return? jp nz,wc_next1 ;no, go on call newline ;yes, newline ret wc_next1: cp 0x08 ;ctrl-h (backspace) jp nz,wc_next2 call backspace ret wc_next2: sub a,0x20 ;adjust ASCII value to location in char table ret c ;non-printing character ASCII < 20h sla a ;multiply by two ld d,a ld a,(test_line) ld b,a ld a,(test_pos) ld c,a ld a,d call put_char ld a,(test_pos) cp 51 ;last place in line? jp z,wc_next3 ;yes, do not inc position, make newline inc a ;no, inc position ld (test_pos),a ret wc_next3: call newline ret ;write_string for system with display ;Writes a zero-terminated string to the display at current cursor location ;Uses calls to write_char, which calls put_char ;So all registers will be used -- need pointer in RAM ;Address of string to write passed in HL write_string: ld (string_pointer),hl ld a,(hl) cp 0 jp z,ws_quit call write_char ld hl,(string_pointer) inc hl ld (string_pointer),hl jp write_string ws_quit: ret ds 14,0 ;to get NMI handler to 0066h ;Need to put non-maskable interrupt (NMI) handler here, at 0066h push af ld a,(timer_tick) inc a ld (timer_tick),a pop af retn ;Binary loader. Receive a binary file over serial port, place in memory. ;Target address passed in HL, length of load (= file length) in BC bload: in a,(3) ;get status and 002h ;check RxRDY bit jp z,bload ;not ready, loop in a,(2) ld (hl),a inc hl dec bc ;byte counter ld a,b ;need to test BC this way because or c ;dec rp instruction does not change flags jp nz,bload ret ; ;Binary dump to port. Send a stream of binary data from memory to serial output ;Address of dump passed in HL, length of dump in BC bdump: in a,(3) ;get status and 001h ;check TxRDY bit jp z,bdump ;not ready, loop ld a,(hl) out (2),a inc hl dec bc ld a,b ;need to test this way because or c ;dec rp instruction does not change flags jp nz,bdump ret ;Subroutine to get a string from console input, place in buffer. ;Buffer address passed in HL reg. ;Uses A,BC,DE,HL registers (including calls to other subroutines). ;Line entry ends by hitting return key. Return char not included in string (replaced by zero). ;Backspace editing OK. No error checking. ; get_line: ld (buffer_pointer),hl ;put buffer address in variable ld a,0FFh ld (blink_flag),a ;blink cursor while waiting for input out (5),a ;clear shift reg (get_char mode 0) get_line_next_char: call get_char cp 0dh ;check if return jp z,get_line_done ;yes, normal exit cp 07fh ;check if backspace (VT102 keys) jp z,get_line_backspace ;yes, jump to backspace routine cp 008h ;check if backspace (ANSI keys) jp z,get_line_backspace ;yes, jump to backspace ld (get_line_char),a ;store character call write_char ;put char on screen ld a,(get_line_char) ;get char back ld hl,(buffer_pointer) ld (hl),a ;store char in buffer inc hl ;point to next space in buffer ld (buffer_pointer),hl ld a,0 ld (hl),a ;leaves a zero-terminated string in buffer jp get_line_next_char get_line_backspace: ld a,(test_pos) ;check if at beginning of line cp 0 jp z,get_line_next_char ;yes, ignore backspace, get next char ld hl,(buffer_pointer) ;no, erase char from buffer ld a,0 ;put a zero in buffer where the last char was ld (hl),a dec hl ld (buffer_pointer),hl call backspace ;backspaces display, rubs out char jp get_line_next_char get_line_done: ld a,0 ;turn blinking off ld (blink_flag),a ret ;byte_to_hex_string subroutine ;Creates a two-char hex string from the byte value passed in register A ;Location to place string passed in HL ;String is zero-terminated, stored in 3 locations starting at HL ;Also uses registers b,d, and e byte_to_hex_string: ld b,a ;store original byte srl a ;shift right 4 times, putting srl a ;high nybble in low-nybble spot srl a ;and zeros in high-nybble spot srl a ld d,000h ;prepare for 16-bit addition ld e,a ;de contains offset push hl ;temporarily store string target address ld hl,hex_char_table ;use char table to get high-nybble character add hl,de ;add offset to start of table ld a,(hl) ;get char pop hl ;get string target address ld (hl),a ;store first char of string inc hl ;point to next string target address ld a,b ;get original byte back from reg b and 00fh ;mask off high-nybble ld e,a ;d still has 000h, now de has offset push hl ;temp store string target address ld hl,hex_char_table ;start of table add hl,de ;add offset ld a,(hl) ;get char pop hl ;get string target address ld (hl),a ;store second char of string inc hl ;point to third location ld a,000h ;zero to terminate string ld (hl),a ;store the zero ret ;done ;hex_char_to_nybble subroutine ;Converts a single ASCII hex char to a nybble value ;Pass char in reg A. Letter numerals must be upper case. ;Return nybble value in low-order reg A with zeros in high-order nybble if no error. ;Return 0ffh in reg A if error (char not a valid hex numeral). ;Also uses b, c, and hl registers. hex_char_to_nybble: ld hl,hex_char_table ld b,00fh ;no. of valid characters in table - 1. ld c,000h ;will be nybble value hex_to_nybble_loop: cp (hl) ;character match here? jp z,hex_to_nybble_ok ;match found, exit dec b ;no match, check if at end of table jp m,hex_to_nybble_err ;table limit exceded, exit with error inc c ;still inside table, continue search inc hl jp hex_to_nybble_loop hex_to_nybble_ok: ld a,c ;put nybble value in a ret hex_to_nybble_err: ld a,0ffh ;error value ret ;hex_to_byte subroutine ;Converts a hex character pair to a byte value ;Called with location of high-order char in HL ;If no error carry flag clear, returns with byte value in register A, and ;HL pointing to next mem location after char pair. ;If error (non-hex char) carry flag set, HL pointing to invalid char hex_to_byte: ld a,(hl) ;location of character pair push hl ;store hl (hex_char_to_nybble uses it) call hex_char_to_nybble pop hl ;returns with nybble value in a reg, or 0ffh if error cp 0ffh ;non-hex character? jp z,hex_to_byte_err ;yes, exit with error sla a ;no, move low order nybble to high side sla a sla a sla a ld d,a ;store high-nybble inc hl ;get next character of the pair ld a,(hl) push hl ;store hl call hex_char_to_nybble pop hl cp 0ffh ;non-hex character? jp z,hex_to_byte_err ;yes, exit with error or d ;no, combine with high-nybble inc hl ;point to next memory location after char pair scf ccf ;no-error exit (carry = 0) ret hex_to_byte_err: scf ;error, carry flag set ret hex_char_table: defm "0123456789ABCDEF" ;ASCII hex table ;address_entry subroutine ;Subroutine to get a two-byte address from keyboard input. ;Returns with address value in HL ;Uses locations in RAM for buffer and variables address_entry: ld hl,buffer ;location for entered string call get_line ;returns with address string in buffer ld hl,buffer ;location of stored address entry string call hex_to_byte ;will get high-order byte first jp c, address_entry_error ;if error, jump ld (current_location+1),a ;store high-order byte, little-endian ld hl,buffer+2 ;point to low-order hex char pair call hex_to_byte ;get low-order byte jp c, address_entry_error ;jump if error ld (current_location),a ;store low-order byte in lower memory ld hl,(current_location) ;put memory address in hl ret address_entry_error: ld hl,address_error_msg call write_string jp address_entry ;decimal_entry subroutine ;Gets a decimal string from input, returns a word value ;Calls decimal_string_to_word subroutine decimal_entry: ld hl,buffer call get_line ld de,(buffer_pointer) ;pointer to end of string ld hl,buffer call decimal_string_to_word ret nc ;no error, return with word in hl ld hl,decimal_error_msg ;error, try again call write_string jp decimal_entry ;decimal_string_to_word subroutine ;Converts a decimal string to a word value ;Call with address of string in HL, pointer to end of string in DE ;Carry flag set if error (non-decimal char) ;Carry flag clear, word value in HL if no error. decimal_string_to_word: ld b,d ld c,e ;use BC as string pointer ld (current_location),hl ;store addr. of start of buffer in RAM word variable ld hl,000h ;starting value zero ld (current_value),hl ld hl,decimal_place_value ;pointer to values ld (value_pointer),hl decimal_next_char: dec bc ;next char in string (moving right to left) ld hl,(current_location) ;check if at end of decimal string scf ;get ready to subtract de from buffer addr. ccf ;set carry to zero (clear) sbc hl,bc ;keep going if bc > or = hl (buffer address) jp c,decimal_continue ;borrow means bc > hl jp z,decimal_continue ;z means bc = hl ld hl,(current_value) ;return if de < buffer address (no borrow) scf ;get value back from RAM variable ccf ret ;return with carry clear, value in hl decimal_continue: ld a,(bc) ;next char in string (right to left) sub 030h ;ASCII value of zero char jp m,decimal_error ;error if char value less than 030h cp 00ah ;error if byte value > or = 10 decimal jp p,decimal_error ;a reg now has value of decimal numeral ld hl,(value_pointer) ;get value to add an put in de ld e,(hl) ;little-endian (low byte in low memory) inc hl ld d,(hl) inc hl ;hl now points to next value ld (value_pointer),hl ld hl,(current_value) ;get back current value decimal_add: dec a ;add loop to increase total value jp m,decimal_add_done ;end of multiplication add hl,de jp decimal_add decimal_add_done: ld (current_value),hl jp decimal_next_char decimal_error: scf ret jp decimal_add decimal_place_value: defw 1,10,100,1000,10000 ;Memory dump subroutine ;Displays a 256-byte block of memory in 16-byte rows. ;Called with address of start of block in HL memory_dump: ld (current_location),hl ;store address of block to be displayed ld a,0 ld (byte_count),a ;initialize byte count ld (line_count),a ;initialize line count jp dump_new_line dump_next_byte: ld hl,(current_location) ;get byte address from storage, ld a,(hl) ;get byte to be converted to string inc hl ;increment address and ld (current_location),hl ;store back ld hl,buffer ;location to store string call byte_to_hex_string ;convert ld hl,buffer ;display string call write_string ld a,(byte_count) ;next byte inc a jp z,dump_done ;stop when 256 bytes displayed ld (byte_count),a ;not finished yet, store ld a,(line_count) ;end of line (16 bytes)? cp 00fh ;yes, start new line jp z,dump_new_line inc a ;no, increment line count ld (line_count),a ld a,020h ;print space call write_char jp dump_next_byte ;continue dump_new_line: ld a,0 ;reset line count to zero ld (line_count),a ;Original subroutine had newline here, but with display limited to 52 characters ;it is not needed because write_string subroutine produces a newline when the line is full ; call newline ;no need for newline here ld hl,(current_location) ;location of start of line ld a,h ;high byte of address ld hl,buffer call byte_to_hex_string ;convert ld hl,buffer call write_string ;write high byte ld hl,(current_location) ld a,l ;low byte of address ld hl,buffer call byte_to_hex_string ;convert ld hl,buffer call write_string ;write low byte ld a,020h ;space call write_char jp dump_next_byte ;now write 16 bytes dump_done: ld a,000h ld hl,buffer ld (hl),a ;clear buffer of last string call newline ret ; ;Memory load subroutine ;Loads RAM memory with bytes entered as hex characters ;Called with address to start loading in HL ;Displays entered data in 16-byte rows. memory_load: ld (current_location),hl ld hl,data_entry_msg call write_string jp load_new_line load_next_char: out (5),a ;get_char mode 0 call get_char cp 00dh ;return? jp z,load_done ;yes, quit ld (buffer),a out (5),a ;get_char mode 0 call get_char cp 00dh ;return? jp z,load_done ;yes, quit ld (buffer+1),a ld hl,buffer call hex_to_byte jp c,load_data_entry_error ;non-hex character ld hl,(current_location) ;get byte address from storage, ld (hl),a ;store byte inc hl ;increment address and ld (current_location),hl ;store back ld a,(buffer) call write_char ld a,(buffer+1) call write_char ld a,(line_count) ;end of line (16 characters)? cp 00fh ;yes, start new line jp z,load_new_line inc a ;no, increment line count ld (line_count),a ld a,020h ;print space call write_char jp load_next_char ;continue load_new_line: ld a,000h ;reset line count to zero ld (line_count),a call newline jp load_next_char ;continue load_data_entry_error: call newline ld hl,data_error_msg call write_string ret load_done: call newline ret ; ;Strings used in subroutines length_entry_string: defm "Enter length of file to load (decimal): ",0 dump_entry_string: defm "Enter no. of bytes to dump (decimal): ",0 address_entry_msg: defm "Enter 4-digit hex address (use upper-case A through F): ",0 address_error_msg: defm "\rError: invalid hex character, try again: ",0 data_entry_msg: defm "Enter hex bytes, hit return when finished.\r",0 data_error_msg: defm "Error: invalid hex byte.\r",0 decimal_error_msg: defm "\rError: invalid decimal number, try again: ",0 ; ;Simple monitor program for CPUville Z80 computer with serial interface. monitor_cold_start: ld sp,ROM_monitor_stack ld a,0 ;initialize variables ld (shift_down),a ;set shift down false ld (ctrl_down),a ;set ctrl down false ld (clear_flag),a ;set clear flag false ld (test_char),a ;initialize cursor position ld (test_pos),a ld (test_line),a ld (capslock),a ;set capslock false call clear_screen call initialize_offset_table call initialize_port ld hl,monitor_message call write_string ld a,0 ld (timer_tick),a mcs_delay_loop: ld a,(timer_tick) cp 40 ;about 5 sec delay to allow keyboard to jp c,mcs_delay_loop ; initialize at power-on monitor_warm_start: call newline ;routine program return here to avoid re-initialization of port ld a,03eh ;prompt symbol call write_char ld hl,buffer call get_line ;get monitor input string (command) call newline call parse ;interprets command, returns with address to jump to in HL jp (hl) ; ;Parses an input line stored in buffer for available commands as described in parse table. ;Returns with address of jump to action for the command in HL parse: ld bc,parse_table ;bc is pointer to parse_table parse_start: ld a,(bc) ;get pointer to match string from parse table ld e,a inc bc ld a,(bc) ld d,a ;de will is pointer to strings for matching ld a,(de) ;get first char from match string or 000h ;zero? jp z,parser_exit ;yes, exit no_match ld hl,buffer ;no, parse input string match_loop: cp (hl) ;compare buffer char with match string char jp nz,no_match ;no match, go to next match string or 000h ;end of strings (zero)? jp z,parser_exit ;yes, matching string found inc de ;match so far, point to next char in match string ld a,(de) ;get next character from match string inc hl ;and point to next char in input string jp match_loop ;check for match no_match: inc bc ;skip over jump target to inc bc inc bc ;get address of next matching string jp parse_start parser_exit: inc bc ;skip to address of jump for match ld a,(bc) ld l,a inc bc ld a,(bc) ld h,a ;returns with jump address in hl ret ; ;Actions to be taken on match ; ;Memory dump program ;Input 4-digit hexadecimal address ;Calls memory_dump subroutine dump_jump: ld hl,dump_message ;Display greeting call write_string ld hl,address_entry_msg ;get ready to get address call write_string call address_entry ;returns with address in HL ld (current_location),hl call newline ld hl,(current_location) call memory_dump jp monitor_warm_start ; ;Hex loader, displays formatted input load_jump: ld hl,load_message ;Display greeting call write_string ;get address to load ld hl,address_entry_msg ;get ready to get address call write_string call address_entry ld (current_location),hl call newline ld hl,(current_location) call memory_load jp monitor_warm_start ; ;Jump and run do the same thing: get an address and jump to it. run_jump: ld hl,run_message ;Display greeting call write_string ld hl,address_entry_msg ;get ready to get address call write_string call address_entry jp (hl) ; ;Help and ? do the same thing, display the available commands help_jump: ld hl,help_message call write_string ld hl,parse_table ;table with pointers to command strings help_loop: ld a,(hl) ;displays the strings for matching commands by ld c,a ; getting the string addresses from the inc hl ; parse table ld a,(hl) ;pass address of string to hl through a reg ld b,a ld a,(bc) ;bc now points to start of match string or 0 ;exit if no_match string jp z,help_done ld (help_table_pointer),hl ld (help_string_pointer),bc ld a,020h ;space char call write_char ld hl,(help_string_pointer) call write_string ;writes match string ld hl,(help_table_pointer) inc hl ;pass over jump address in table inc hl inc hl jp help_loop help_done: jp monitor_warm_start ; ;Binary file load. Need both address to load and length of file bload_jump: ld hl,bload_message call write_string ld hl,address_entry_msg call write_string call address_entry ld (load_address),hl call newline ld hl,length_entry_string call write_string call decimal_entry ld (load_length),hl ld hl,bload_ready_message call write_string ld bc,(load_length) ld hl,(load_address) call bload jp monitor_warm_start ; ;Binary memory dump. Need address of start of dump and no. bytes bdump_jump: ld hl,bdump_message call write_string ld hl,address_entry_msg call write_string call address_entry ld (bdump_address),hl call newline ld hl,dump_entry_string call write_string call decimal_entry ld (bdump_length),hl ld hl,bdump_ready_message call write_string out (5),a ;get_char mode 0 call get_char ld hl,(bdump_address) ld bc,(bdump_length) call bdump jp monitor_warm_start ;Tiny BASIC jump ;Pure jump, so when done with BASIC need to reset computer basic_jump: jp tiny_basic ;Prints message for no match to entered command no_match_jump: ld hl,no_match_message call write_string ld hl, buffer call write_string jp monitor_warm_start ;Put character subroutine ;A has character location in table (ASCII value - 0x20) x 2 ;B has line number, from 0 to 23 ;C has character postion in line, from 0 to 51 ;Does not alter the global line and position variables put_char: ld (char),a ld a,b ld (char_line),a ld a,c ld (char_pos),a ;Calculate upper-left (anchor) dot location of character ld a,(char_line) ld b,a ld a,0 ;starting dot_y pc_loop1: dec b jp m,pc_next1 add a,10 ;width of character line = 10 display lines jp pc_loop1 pc_next1: ld (anchor_dot_y),a ld de,0 ;starting dot_x ld a,(char_pos) ld b,a pc_loop2: dec b jp m,pc_next2 ld a,e add a,6 ld e,a ld a,0 adc a,d ld d,a jp pc_loop2 pc_next2: ld (anchor_dot_x),de ;Put each dot of the character on the screen ;Get dot_x and dot_y offsets from character offset table ;Offsets added to anchor dot x and y values ;When byte in character offset table = 0xFF, done ld a,0 ld (dot_count),a pc_loop3: ld a,(char) ld hl,char_table add a,l ld l,a ld a,0 adc a,h ld h,a ld a,(hl) ;lsb of char_offsets address ld e,a inc hl ld a,(hl) ;msb of char_offsets address ld d,a ld h,d ld l,e ld a,(dot_count) add a,l ld l,a ld a,0 adc a,h ld h,a ld a,(hl) ;get offset value cp 0xff ;quit if at end of offsets list jp z,pc_done ld b,a ;add y offset to anchor value ld a,(anchor_dot_y) add a,b ld (dot_y),a inc hl ld a,(hl) ld de,(anchor_dot_x) add a,e ;add x offset to anchor value ld e,a ld a,0 adc a,d ld d,a ld (dot_x),de inc hl ld a,(clear_flag) ;for backspacing, to erase a char cp 0xff jp z,pc_next3 ld a,(dot_y) ld bc,(dot_x) call set_dot jp pc_next4 pc_next3: ld a,(dot_y) ld bc,(dot_x) call clear_dot pc_next4: ld a,(dot_count) inc a inc a ld (dot_count),a jp pc_loop3 pc_done: ret ;Initialize offset table subroutine ;Creates a table of addresses of the first byte in each line of display ;240 lines, from 0 to 239 ;Each table entry is a two-byte address, in little-endian ;Table size is 2 x 240 = 480 bytes ;Puts data into line_offset_table, which has to be declared in global variables section ;Uses hl, de, ix, and b registers initialize_offset_table: ld hl,vram_start ld b,239 ld de,40 ld ix,line_offset_table iot_loop1: ld (ix+0),l inc ix ld (ix+0),h inc ix djnz iot_next1 ;uses b as counter ret iot_next1: add hl,de ;step address by 40 jp iot_loop1 ;Clear screen subroutine clear_screen: ld a,0x00 ld b,a ld hl,vram_start cs_loop: ld a,b ld (hl),a ld a,h cp 0xa5 jp nz,cs_next ld a,l cp 0x7f ret z ;done cs_next: inc hl jp cs_loop ;set_dot subroutine ;Places (sets) a dot at co-ordinates (dot number:line number) ;dot number is x, line number is y ;Line number between 0 (top line) and 239 (bottom line) ;Dot number between 0 (left edge) and 319 (right edge) ;line number passed in A register ;dot number passed in BC register ;Uses registers a, b, c, d, e, h, l set_dot: ld hl,line_offset_table ld d,a ;temp store line number add a,l ld l,a ;start 16-bit addition of 2x line number ld a,0 adc a,h ld h,a ld a,d ;get line number back add a,l ;add again ld l,a ld a,0 adc a,h ld h,a ;hl has pointer to address in table ld e,(hl) inc hl ld d,(hl) ;de has address of start of line in vram sd_next1: ld a,c ;divide dot number by 8 to get byte address srl a ;shift lsb of dot number srl a srl a ;lop off right 3 bits (bit number in byte) srl b ;test if bit number 256 or greater jp nc,sd_next2 ;no, finish address add a,32 ;yes, add 32 to offset sd_next2: add a,e ;add offset to line address in de ld e,a ;by 16-bit addition ld a,0 adc a,d ld d,a ;de has address of byte ld a,c ;get lsb of dot number and 0x07 ;mask off all but bit number ld hl,dot_table ;table with bit masks add a,l ld l,a ld a,0 adc a,h ld h,a ld a,(hl) ;a now has bit mask ld b,a ;put the mask in b ld h,d ;put byte address in hl ld l,e ld a,(hl) ;get the byte from VRAM or b ;set the bit ld (hl),a ;store back ret ;clear_dot subroutine ;Clears (unsets) a dot at co-ordinates (dot number:line number) ;dot number is x, line number is y ;Line number between 0 (top line) and 239 (bottom line) ;Dot number between 0 (left edge) and 319 (right edge) ;line number passed in A register ;dot number passed in BC register ;Uses registers a, b, c, d, e, h, l clear_dot: ld hl,line_offset_table ld d,a ;temp store line number add a,l ld l,a ;start 16-bit addition of 2x line number ld a,0 adc a,h ld h,a ld a,d ;get line number back add a,l ;add again ld l,a ld a,0 adc a,h ld h,a ;hl has pointer to address in table ld e,(hl) inc hl ld d,(hl) ;de has address of start of line in vram cd_next1: ld a,c ;divide dot number by 8 to get byte address srl a ;shift lsb of dot number srl a srl a ;lop off right 3 bits (bit number in byte) srl b ;test if bit number 256 or greater jp nc,cd_next2 ;no, finish address add a,32 ;yes, add 32 to offset cd_next2: add a,e ;add offset to line address in de ld e,a ;by 16-bit addition ld a,0 adc a,d ld d,a ;de has address of byte ld a,c ;get lsb of dot number and 0x07 ;mask off all but bit number ld hl,dot_table ;table with bit masks add a,l ld l,a ld a,0 adc a,h ld h,a ld a,(hl) ;a now has bit mask cpl ;invert it ld b,a ;put the mask in b ld h,d ;put byte address in hl ld l,e ld a,(hl) ;get the byte from VRAM and b ;clear the bit ld (hl),a ;store back ret ;get_char subroutine, adapted from keyboard-input.asm ;Program for video display system keyboard interface ;Gets keyboard scan code from keypress, converts to ASCII character if possible ;Two modes: ;Mode 0, call to get_char preceded by out (5),a to clear shift reg ;Mode 1, call has no precedent ;In mode 0, get_char will wait for a keypress ;In mode 1, get_char may or may not wait for a keypress ;In mode 1, if a keycode is already present, it will return without waiting for a keypress ;In mode 1, if a keycode is not present, it will wait ;Uses get_keycode subroutine ;Uses A, B, and HL registers ;Returns ASCII in register A, or 0 if no character comes from keypress (i.e. shift) get_char: call get_keycode cp 0F0h ;key break jp z,ki_break cp 0E0h ;long keycodes jp z,ki_long_keycodes cp 12h ;left shift key jp z,ki_shift_make cp 59h ;right shift key jp z,ki_shift_make cp 14h ;left ctrl key jp z,ki_ctrl_make cp 58h ;caps lock jp z,ki_capslock_make ;Check if ctrl key is down ;If so, return ctrl-characters ld b,a ;temp store keycode ld a,(ctrl_down) cp 0FFh ;is ctrl- key down? jp z,ki_ctrl_char ;yes, create ctrl-char jp ki_next_1 ;no, skip to regular ASCII ki_ctrl_char: ld a,b ;get keycode back cp 1ah ;Z key jp nz,ki_ctrl_next1 ld a,1ah ;ctrl-Z jp ki_output ki_ctrl_next1: cp 1ch ;A key jp nz,ki_ctrl_next2 ld a,01h ;ctrl-A jp ki_output ki_ctrl_next2: cp 21h ;C key jp nz,ki_ctrl_next3 ld a,03h ;ctrl-C jp ki_output ki_ctrl_next3: cp 22h ;X key jp nz,ki_ctrl_next4 ld a,18h ;ctrl-X jp ki_output ki_ctrl_next4: cp 44h ;O key jp nz,ki_null_output ;control char currently undefined ld a,0Fh ;ctrl-O jp ki_output ;See if shift key is down or caps lock or both and use appropriate table ki_next_1: ld a,(shift_down) cp 0FFh ;is shift key down? jp z,ki_shifted ;yes, use shifted table ld a,(capslock) ;is capslock? cp 0FFh jp z,ki_capslock ;yes, use capslock table ;unshifted ASCII ld a,b ;no, use unshifted table cp 80h ;check if keycode out of bounds jp nc,ki_null_output ld hl,unshifted_ascii_table add a,l ld l,a ld a,0 adc a,h ld h,a ld a,(hl) jp ki_output ki_shifted: ld a,(capslock) cp 0FFh ;is caps lock? jp z,ki_shiftcapslock ;yes, use shift + capslock table ld a,b ;get keycode back cp 80h ;check if keycode out of bounds jp nc,ki_null_output ld hl,shifted_ascii_table add a,l ld l,a ld a,0 adc a,h ld h,a ld a,(hl) jp ki_output ki_capslock: ld a,b ;get keycode back cp 80h ;check if keycode out of bounds jp nc,ki_null_output ld hl,capslock_ascii_table add a,l ld l,a ld a,0 adc a,h ld h,a ld a,(hl) jp ki_output ki_shiftcapslock: ld a,b ;get keycode back cp 80h ;check if keycode out of bounds jp nc,ki_null_output ld hl,shiftcapslock_ascii_table add a,l ld l,a ld a,0 adc a,h ld h,a ld a,(hl) jp ki_output ki_break: call get_keycode ;get identity of key that caused break cp 12h ;left shift key jp z,ki_shift_break cp 59h ;right shift key jp z,ki_shift_break cp 14h ;ctrl key (either -- can come from long keycodes too) jp z,ki_ctrl_break jp ki_null_output ;ignore other breaks ki_long_keycodes: call get_keycode ;get next keycode value cp 0F0h ;long keycode break jp z,ki_break cp 14h ;E0 14 is right ctrl key jp z,ki_ctrl_make cp 71h ld a,7Fh ;delete jp z,ki_output jp ki_null_output ;ignore other long keycodes for now ki_ctrl_make: ld a,0ffh ld (ctrl_down),a jp ki_null_output ki_shift_make: ld a,0ffh ld (shift_down),a jp ki_null_output ki_ctrl_break: ld a,00h ld (ctrl_down),a jp ki_null_output ki_shift_break: ld a,00h ld (shift_down),a jp ki_null_output ki_capslock_make: ld a,(capslock) cpl ld (capslock),a jp ki_null_output ;no need to wait for another keycode ki_null_output: jp get_char ;exit only when have ASCII ki_output: ret ;exit here when have ASCII ;get_keycode subroutine ;Keyboard output is 11 serial bits, inverted ;Output goes through two shift registers in a row ;Shift registers configured as input ports 4 and 5 ;Once 11 bits received and complemented they are: ;Read port 4 returns: ; D7 = Stop bit ; D6 = Parity bit ; D5 to D0 = keycode bits D7 to D2 ;Read port 5 returns: ; D7 = keycode bit D1 ; D6 = keycode bit D0 ; D5 = Start bit ; D4 to D0 = 0 ;Write port 5 clears both shift registers ;Polls port 5 for D5 = 1 (start bit of 11-bit keyboard output) ;Code in central loop to blink cursor while awaiting input ;Returns keycode in A register get_keycode: ld a,4 ld (timer_tick),a ;set initial count for blinking gk_loop1: ld a,(blink_flag) cp 0FFh jp nz,gk_next1 ;don't blink cursor ld a,(timer_tick) cp 08h ;blink every 8 ticks jp nz,gk_next1 ;don't blink yet ;code to blink character at cursor location ;clear_flag used by put_char to determine if character is to be written or erased ld a,0 ;OK, blink ld (timer_tick),a ;reset timer ld a,(clear_flag) ;flip clear flag cpl ld (clear_flag),a ld a,(test_pos) ;write or erase underline char ld c,a ld a,(test_line) ld b,a ld a,7Eh ;underline char in table call put_char gk_next1: in a,(5) ;polling loop for start bit and 20h ;check for start bit = 1 jp z,gk_loop1 ;no, keep looking in a,(4) ;yes, get shift register contents cpl sla a ;shift left twice sla a ld b,a ;temp storage in a,(5) ;get shift register contents out (5),a ;clear shift registers cpl srl a ;logical shift right 6 times srl a srl a srl a srl a srl a or b ;combine with upper part push af ;keycode in a reg, store keycode ld a,(blink_flag) ;if blinking, need to clear underline char cp 0FFh ;was blink flag true? jp nz,gk_done ;no, not blinking, done ld a,0FFh ;yes, make clear flag true ld (clear_flag),a ld a,(test_pos) ;erase underline char ld c,a ld a,(test_line) ld b,a ld a,7Eh ;underline char in table call put_char ld a,0 ;make clear flag false ld (clear_flag),a gk_done: pop af ;get keycode back ret ;keycode will be in A reg ;newline subroutine ;increases line number by one ;if line off screen, scrolls ;puts cursor back to beginning of next line newline: ld a,0 ld (test_pos),a ;cursor to start of line ld a,(test_line) inc a cp 24 ;off bottom of screen jp nz,nl_next1 call scroll ret nl_next1: ld (test_line),a ret ;scroll subroutine ;called if line = 24 (off screen) ;copies vram bytes from location to location-400 (ten scan lines above) ;starts at vram + 400 (first byte in line 1) ;copies 9600 - 400 bytes = 9200 bytes, counter in de ;when finished, blanks line 23 and puts cursor at beginning of line scroll: ld de,9200 ;no. bytes to copy ld hl,vram_start + 400 ;source ld bc,vram_start ;target scroll_loop1: ld a,(hl) ld (bc),a dec de ;zero flag not affected so ld a,d ;check for zero this way or e jp z,scroll_done inc hl inc bc jp scroll_loop1 ;done moving, clear bottom line scroll_done: ld a,23 ld (test_line),a ;last line of display call clear_line ret ;clear_line subroutine ;clears the line given by test_line ;does not alter test_line ;sets cursor at start of line clear_line: ld hl,vram_start ld bc,400 ;index value ld a,(test_line) ;adjust address to first byte in line to be cleared cl_loop1: cp 0 ;done, go on jp z,cl_loop2 add hl,bc dec a jp cl_loop1 cl_loop2: ld a,0 ld (hl),a inc hl dec bc ;bc starts with 400, from above ld a,b or c ;check for zero this way jp nz,cl_loop2 ld a,0 ;set cursor to start of line ld (test_pos),a ret ;backspace subroutine ;moves cursor back one space, erases character ;Stops if at beginning of line backspace: ld a,(test_pos) cp 0 ;at start of line, can't backspace ret z dec a ld (test_pos),a ld c,a ld a,(test_line) ld b,a ld a,0xff ld (clear_flag),a ;causes put_char to clear instead of set dots ld a,190 ;block character in table call put_char ld a,0 ld (clear_flag),a ret ;Monitor data structures: monitor_message: defm "\rStandalone Z80 computer ROM ver. 1\r",0 no_match_message: defm "? ",0 help_message: defm "Commands implemented:\r",0 dump_message: defm "Displays a 256-byte block of memory.\r",0 load_message: defm "Enter hex bytes starting at memory location.\r",0 run_message: defm "Will jump to (execute) program at address entered.\r",0 bload_message: defm "Loads a binary file into memory.\r",0 bload_ready_message: defm "\rReady to receive, start transfer.",0 bdump_message: defm "Dumps binary data from memory to serial port.\r",0 bdump_ready_message: defm "\rReady to send, hit any key to start.",0 ;Strings for matching: dump_string: defm "dump",0 load_string: defm "load",0 jump_string: defm "jump",0 run_string: defm "run",0 question_string: defm "?",0 help_string: defm "help",0 bload_string: defm "bload",0 bdump_string: defm "bdump",0 basic_string: defm "basic",0 no_match_string: defm 0,0 ;Table for matching strings to jumps parse_table: defw dump_string,dump_jump,load_string,load_jump defw jump_string,run_jump,run_string,run_jump defw question_string,help_jump,help_string,help_jump defw bload_string,bload_jump,bdump_string,bdump_jump defw basic_string,basic_jump,no_match_string,no_match_jump ;ASCII tables ;Add keycode value to address to retrieve ASCII equivalent ;Tables for unshifted, shifted, capslock, and shift+capslock characters unshifted_ascii_table: db 0,0,0,0,0,0,0,0,0,0,0,0,0,09h,60h,0 db 0,0,0,0,0,71h,31h,0,0,0,7Ah,73h,61h,77h,32h,0 db 0,63h,78h,64h,65h,34h,33h,0,0,20h,76h,66h,74h,72h,35h,0 db 0,6Eh,62h,68h,67h,79h,36h,0,0,0,6Dh,6Ah,75h,37h,38h,0 db 0,2Ch,6Bh,69h,6Fh,30h,39h,0,0,2Eh,2Fh,6Ch,3Bh,70h,2Dh,0 db 0,0,27h,0,5Bh,3Dh,0,0,0,0,0Dh,5Dh,0,5Ch,0,0 db 0,0,0,0,0,0,08h,0,0,31h,0,34h,37h,0,0,0 db 30h,2Eh,32h,35h,36h,38h,1Bh,0,0,2Bh,33h,2Dh,2Ah,39h,0,0 shifted_ascii_table: db 0,0,0,0,0,0,0,0,0,0,0,0,0,09h,7Eh,0 db 0,0,0,0,0,51h,21h,0,0,0,5Ah,53h,41h,57h,40h,0 db 0,43h,58h,44h,45h,24h,23h,0,0,20h,56h,46h,54h,52h,25h,0 db 0,4Eh,42h,48h,47h,59h,5Eh,0,0,0,4Dh,4Ah,55h,26h,2Ah,0 db 0,3Ch,4Bh,49h,4Fh,29h,28h,0,0,3Eh,3Fh,4Ch,3Ah,50h,5Fh,0 db 0,0,22h,0,7Bh,2Bh,0,0,0,0,0Dh,7Dh,0,7Ch,0,0 db 0,0,0,0,0,0,08h,0,0,31h,0,34h,37h,0,0,0 db 30h,2Eh,32h,35h,36h,38h,1Bh,0,0,2Bh,33h,2Dh,2Ah,39h,0,0 capslock_ascii_table: db 0,0,0,0,0,0,0,0,0,0,0,0,0,09h,60h,0 db 0,0,0,0,0,51h,31h,0,0,0,5Ah,53h,41h,57h,32h,0 db 0,43h,58h,44h,45h,34h,33h,0,0,20h,56h,46h,54h,52h,35h,0 db 0,4Eh,42h,48h,47h,59h,36h,0,0,0,4Dh,4Ah,55h,37h,38h,0 db 0,2Ch,4Bh,49h,4Fh,30h,39h,0,0,2Eh,2Fh,4Ch,3Bh,50h,2Dh,0 db 0,0,60h,0,5Bh,3Dh,0,0,0,0,0Dh,5Dh,0,5Ch,0,0 db 0,0,0,0,0,0,08h,0,0,31h,0,34h,37h,0,0,0 db 30h,2Eh,32h,35h,36h,38h,1Bh,0,0,2Bh,33h,2Dh,2Ah,39h,0,0 shiftcapslock_ascii_table: db 0,0,0,0,0,0,0,0,0,0,0,0,0,09h,7Eh,0 db 0,0,0,0,0,71h,21h,0,0,0,7Ah,73h,61h,77h,40h,0 db 0,63h,78h,64h,65h,24h,23h,0,0,20h,76h,66h,74h,72h,25h,0 db 0,6Eh,62h,68h,67h,79h,5Eh,0,0,0,6Dh,6Ah,75h,26h,2Ah,0 db 0,3Ch,6Bh,69h,6Fh,29h,28h,0,0,3Eh,3Fh,6Ch,3Ah,70h,5Fh,0 db 0,0,22h,0,7Bh,2Bh,0,0,0,0,0Dh,7Dh,0,7Ch,0,0 db 0,0,0,0,0,0,08h,0,0,31h,0,34h,37h,0,0,0 db 30h,2Eh,32h,35h,36h,38h,1Bh,0,0,2Bh,33h,2Dh,2Ah,39h,0,0 ;Tables used by character-creating subroutines char_table: defw space_char_offsets,exclam_char_offsets,quote_char_offsets defw hash_char_offsets,dollar_char_offsets,percent_char_offsets defw ampersand_char_offsets,apost_char_offsets,L_paren_char_offsets defw R_paren_char_offsets,asterix_char_offsets,plus_char_offsets defw comma_char_offsets,minus_char_offsets,period_char_offsets defw forslash_char_offsets,zero_char_offsets,one_char_offsets defw two_char_offsets,three_char_offsets,four_char_offsets,five_char_offsets defw six_char_offsets,seven_char_offsets,eight_char_offsets,nine_char_offsets defw colon_char_offsets,semicolon_char_offsets,less_char_offsets defw equal_char_offsets,greater_char_offsets,question_char_offsets defw at_char_offsets defw A_char_offsets,B_char_offsets,C_char_offsets,D_char_offsets defw E_char_offsets,F_char_offsets,G_char_offsets,H_char_offsets defw I_char_offsets,J_char_offsets,K_char_offsets,L_char_offsets defw M_char_offsets,N_char_offsets,O_char_offsets,P_char_offsets defw Q_char_offsets,R_char_offsets,S_char_offsets,T_char_offsets defw U_char_offsets,V_char_offsets,W_char_offsets,X_char_offsets defw Y_char_offsets,Z_char_offsets,L_bracket_char_offsets defw backslash_char_offsets,R_bracket_char_offsets,carat_char_offsets defw under_char_offsets,accent_char_offsets defw a_char_offsets,b_char_offsets,c_char_offsets,d_char_offsets defw e_char_offsets,f_char_offsets,g_char_offsets,h_char_offsets defw i_char_offsets,j_char_offsets,k_char_offsets,l_char_offsets defw m_char_offsets,n_char_offsets,o_char_offsets,p_char_offsets defw q_char_offsets,r_char_offsets,s_char_offsets,t_char_offsets defw u_char_offsets,v_char_offsets,w_char_offsets,x_char_offsets defw y_char_offsets,z_char_offsets,L_curly_char_offsets,pipe_char_offsets defw R_curly_char_offsets,tilde_char_offsets,block_char_offsets defw square_char_offsets A_char_offsets: defb 0,1,0,2,0,3,1,0,1,4,2,0,2,4,3,0,3,1,3,2,3,3,3,4,4,0,4,4,5,0,5,4,6,0,6,4,0xFF B_char_offsets: defb 0,0,0,1,0,2,0,3,1,0,1,4,2,0,2,4,3,0,3,1,3,2,3,3,4,0,4,4,5,0,5,4,6,0,6,1,6,2,6,3,0xFF C_char_offsets: defb 0,1,0,2,0,3,1,0,1,4,2,0,3,0,4,0,5,0,5,4,6,1,6,2,6,3,0xFF D_char_offsets: defb 0,0,0,1,0,2,0,3,1,0,1,4,2,0,2,4,3,0,3,4,4,0,4,4,5,0,5,4,6,0,6,1,6,2,6,3,0xFF E_char_offsets: defb 0,0,0,1,0,2,0,3,0,4,1,0,2,0,3,0,3,1,3,2,3,3,4,0,5,0,6,0,6,1,6,2,6,3,6,4,0xFF F_char_offsets: defb 0,0,0,1,0,2,0,3,0,4,1,0,2,0,3,0,3,1,3,2,3,3,4,0,5,0,6,0,0xFF G_char_offsets: defb 0,1,0,2,0,3,1,0,1,4,2,0,3,0,3,2,3,3,4,0,4,4,5,0,5,4,6,1,6,2,6,3,0xFF H_char_offsets: defb 0,0,0,4,1,0,1,4,2,0,2,4,3,0,3,1,3,2,3,3,3,4,4,0,4,4,5,0,5,4,6,0,6,4,0xFF I_char_offsets: defb 0,2,0,3,0,4,1,3,2,3,3,3,4,3,5,3,6,2,6,3,6,4,0xFF J_char_offsets: defb 0,2,0,3,0,4,1,3,2,3,3,3,4,3,5,0,5,3,6,1,6,2,0xFF K_char_offsets: defb 0,0,0,4,1,0,1,3,2,0,2,2,3,0,3,1,4,0,4,2,5,0,5,3,6,0,6,4,0xFF L_char_offsets: defb 0,0,1,0,2,0,3,0,4,0,5,0,6,0,6,1,6,2,6,3,6,4,0xFF M_char_offsets: defb 0,0,0,4,1,0,1,1,1,3,1,4,2,0,2,2,2,4,3,0,3,4,4,0,4,4,5,0,5,4,6,0,6,4,0xFF N_char_offsets: defb 0,0,0,4,1,0,1,4,2,0,2,1,2,4,3,0,3,2,3,4,4,0,4,3,4,4,5,0,5,4,6,0,6,4,0xFF O_char_offsets: defb 0,1,0,2,0,3,1,0,1,4,2,0,2,4,3,0,3,4,4,0,4,4,5,0,5,4,6,1,6,2,6,3,0xFF P_char_offsets: defb 0,0,0,1,0,2,0,3,1,0,1,4,2,0,2,4,3,0,3,1,3,2,3,3,4,0,5,0,6,0,0xFF Q_char_offsets: defb 0,1,0,2,0,3,1,0,1,4,2,0,2,4,3,0,3,4,4,0,4,2,4,4,5,0,5,3,5,4,6,1,6,2,6,3,6,4,0xFF R_char_offsets: defb 0,0,0,1,0,2,0,3,1,0,1,4,2,0,2,4,3,0,3,1,3,2,3,3,4,0,4,2,5,0,5,3,6,0,6,4,0xFF S_char_offsets: defb 0,1,0,2,0,3,1,0,1,4,2,0,3,1,3,2,3,3,4,4,5,0,5,4,6,1,6,2,6,3,0xFF T_char_offsets: defb 0,0,0,1,0,2,0,3,0,4,1,2,2,2,3,2,4,2,5,2,6,2,0xFF U_char_offsets: defb 0,0,0,4,1,0,1,4,2,0,2,4,3,0,3,4,4,0,4,4,5,0,5,4,6,1,6,2,6,3,0xFF V_char_offsets: defb 0,0,0,4,1,0,1,4,2,0,2,4,3,0,3,4,4,0,4,4,5,1,5,3,6,2,0xFF W_char_offsets: defb 0,0,0,4,1,0,1,4,2,0,2,4,3,0,3,4,4,0,4,2,4,4,5,0,5,1,5,3,5,4,6,0,6,4,0xFF X_char_offsets: defb 0,0,0,4,1,0,1,4,2,1,2,3,3,2,4,1,4,3,5,0,5,4,6,0,6,4,0xFF Y_char_offsets: defb 0,0,0,4,1,0,1,4,2,1,2,3,3,2,4,2,5,2,6,2,0xFF Z_char_offsets: defb 0,0,0,1,0,2,0,3,0,4,1,4,2,3,3,2,4,1,5,0,6,0,6,1,6,2,6,3,6,4,0xFF zero_char_offsets: defb 0,1,0,2,0,3,1,0,1,4,2,0,2,4,3,0,3,2,3,4,4,0,4,4,5,0,5,4,6,1,6,2,6,3,0xFF one_char_offsets: defb 0,2,1,1,1,2,2,2,3,2,4,2,5,2,6,1,6,2,6,3,0xFF two_char_offsets: defb 0,1,0,2,0,3,1,0,1,4,2,4,3,2,3,3,4,1,5,0,6,0,6,1,6,2,6,3,6,4,0xFF three_char_offsets: defb 0,1,0,2,0,3,1,0,1,4,2,4,3,2,3,3,4,4,5,0,5,4,6,1,6,2,6,3,0xFF four_char_offsets: defb 0,2,0,3,1,1,1,3,2,0,2,3,3,0,3,3,4,0,4,1,4,2,4,3,4,4,5,3,6,3,0xFF five_char_offsets: defb 0,0,0,1,0,2,0,3,0,4,1,0,2,0,3,0,3,1,3,2,3,3,4,4,5,4,6,0,6,1,6,2,6,3,0xFF six_char_offsets: defb 0,1,0,2,0,3,1,0,1,4,2,0,3,0,3,1,3,2,3,3,4,0,4,4,5,0,5,4,6,1,6,2,6,3,0xFF seven_char_offsets: defb 0,0,0,1,0,2,0,3,0,4,1,4,2,3,3,2,4,2,5,1,6,1,0xFF eight_char_offsets: defb 0,1,0,2,0,3,1,0,1,4,2,0,2,4,3,1,3,2,3,3,4,0,4,4,5,0,5,4,6,1,6,2,6,3,0xFF nine_char_offsets: defb 0,1,0,2,0,3,1,0,1,4,2,0,2,4,3,1,3,2,3,3,3,4,4,4,5,0,5,4,6,1,6,2,6,3,0xFF space_char_offsets: defb 0xFF colon_char_offsets: defb 2,2,4,2,0xFF period_char_offsets: defb 6,2,0xFF comma_char_offsets: defb 5,2,6,2,7,1,0xFF plus_char_offsets: defb 1,2,2,2,3,0,3,1,3,2,3,3,3,4,4,2,5,2,0xFF minus_char_offsets: defb 3,1,3,2,3,3,0xFF semicolon_char_offsets: defb 2,2,5,2,6,2,7,1,0xFF quote_char_offsets: defb 0,1,0,4,1,1,1,4,0xFF less_char_offsets: defb 0,3,1,2,2,1,3,0,4,1,5,2,6,3,0xFF greater_char_offsets: defb 0,0,1,1,2,2,3,3,4,2,5,1,6,0,0xFF equal_char_offsets: defb 2,1,2,2,2,3,4,1,4,2,4,3,0xFF question_char_offsets: defb 0,1,0,2,0,3,1,0,1,4,2,4,3,3,4,2,6,2,0xFF L_bracket_char_offsets: defb 0,1,0,2,0,3,1,1,2,1,3,1,4,1,5,1,6,1,6,2,6,3,0xFF R_bracket_char_offsets: defb 0,1,0,2,0,3,1,3,2,3,3,3,4,3,5,3,6,1,6,2,6,3,0xFF backslash_char_offsets: defb 0,0,1,1,2,1,3,2,4,3,5,3,6,4,0xFF forslash_char_offsets: defb 0,4,1,3,2,3,3,2,4,1,5,1,6,0,0xFF exclam_char_offsets: defb 0,2,1,2,2,2,3,2,4,2,6,2,0xFF a_char_offsets: defb 2,1,2,2,2,3,3,4,4,1,4,2,4,3,4,4,5,0,5,4,6,1,6,2,6,3,6,4,0xFF b_char_offsets: defb 0,0,1,0,2,0,2,1,2,2,2,3,3,0,3,4,4,0,4,4,5,0,5,4,6,0,6,1,6,2,6,3,0xFF c_char_offsets: defb 2,1,2,2,2,3,3,0,4,0,5,0,6,1,6,2,6,3,0xFF d_char_offsets: defb 0,4,1,4,2,1,2,2,2,3,2,4,3,0,3,4,4,0,4,4,5,0,5,4,6,1,6,2,6,3,6,4,0xFF e_char_offsets: defb 2,1,2,2,2,3,3,0,3,4,4,0,4,1,4,2,4,3,4,4,5,0,6,1,6,2,6,3,6,4,0xFF f_char_offsets: defb 0,2,0,3,1,1,1,4,2,1,3,1,3,2,3,3,4,1,5,1,6,1,0xFF g_char_offsets: defb 2,1,2,2,2,3,3,0,3,4,4,0,4,4,5,0,5,4,6,1,6,2,6,3,6,4,7,4,8,1,8,2,8,3,0xFF h_char_offsets: defb 0,0,1,0,2,0,3,0,3,1,3,2,3,3,4,0,4,4,5,0,5,4,6,0,6,4,0xFF i_char_offsets: defb 1,2,3,2,4,2,5,2,6,3,0xFF j_char_offsets: defb 1,3,3,3,4,3,5,3,6,3,7,0,7,3,8,1,8,2,0xFF k_char_offsets: defb 0,0,1,0,2,0,2,3,3,0,3,2,4,0,4,1,5,0,5,2,6,0,6,3,0xFF l_char_offsets: defb 0,1,1,1,2,1,3,1,4,1,5,1,6,2,0xFF m_char_offsets: defb 2,1,2,3,3,0,3,2,3,4,4,0,4,2,4,4,5,0,5,2,5,4,6,0,6,4,0xFF n_char_offsets: defb 2,0,3,0,3,1,3,2,3,3,4,0,4,4,5,0,5,4,6,0,6,4,0xFF o_char_offsets: defb 2,1,2,2,2,3,3,0,3,4,4,0,4,4,5,0,5,4,6,1,6,2,6,3,0xFF p_char_offsets: defb 2,0,2,1,2,2,2,3,3,0,3,4,4,0,4,4,5,0,5,4,6,0,6,1,6,2,6,3,7,0,8,0,0xFF q_char_offsets: defb 2,1,2,2,2,3,2,4,3,0,3,4,4,0,4,4,5,0,5,4,6,1,6,2,6,3,6,4,7,4,8,4,0xFF r_char_offsets: defb 2,0,3,0,3,1,3,2,3,3,4,0,4,4,5,0,6,0,0xFF s_char_offsets: defb 2,1,2,2,2,3,2,4,3,0,4,1,4,2,4,3,5,4,6,0,6,1,6,2,6,3,0xFF t_char_offsets: defb 0,1,1,1,2,0,2,1,2,2,2,3,3,1,4,1,5,1,6,2,6,3,0xFF u_char_offsets: defb 2,0,2,4,3,0,3,4,4,0,4,4,5,0,5,4,6,1,6,2,6,3,6,4,0xFF v_char_offsets: defb 2,0,2,4,3,0,3,4,4,1,4,3,5,1,5,3,6,2,0xFF w_char_offsets: defb 2,0,2,4,3,0,3,2,3,4,4,0,4,2,4,4,5,0,5,2,5,4,6,1,6,3,0xFF x_char_offsets: defb 2,0,2,4,3,1,3,3,4,2,5,1,5,3,6,0,6,4,0xFF y_char_offsets: defb 2,0,2,4,3,0,3,4,4,0,4,4,5,0,5,4,6,1,6,2,6,3,6,4,7,4,8,1,8,2,8,3,0xFF z_char_offsets: defb 2,0,2,1,2,2,2,3,2,4,3,3,4,2,5,1,6,0,6,1,6,2,6,3,6,4,0xFF hash_char_offsets: defb 1,2,1,4,2,0,2,1,2,2,2,3,2,4,3,1,3,3,4,1,4,3,5,0,5,1,5,2,5,3,5,4,6,0,6,2,0xFF dollar_char_offsets: defb 0,2,1,1,1,2,1,3,1,4,2,0,2,2,3,1,3,2,3,3,4,2,4,4,5,0,5,1,5,2,5,3,6,2,0xFF percent_char_offsets: defb 0,4,1,0,1,1,1,3,2,0,2,1,2,3,3,2,4,1,4,3,4,4,5,1,5,3,5,4,6,0,0xFF ampersand_char_offsets: defb 0,1,0,2,1,0,1,3,2,0,3,1,4,0,4,2,4,4,5,0,5,3,6,1,6,2,6,4,0xFF apost_char_offsets: defb 0,3,1,2,0xFF L_paren_char_offsets: defb 0,3,1,2,2,2,3,2,4,2,5,2,6,3,0xFF R_paren_char_offsets: defb 0,1,1,2,2,2,3,2,4,2,5,2,6,1,0xFF asterix_char_offsets: defb 0,1,0,3,1,2,2,1,2,3,0xFF at_char_offsets: defb 0,1,0,2,0,3,1,0,1,4,2,0,2,3,2,4,3,0,3,2,3,4,4,0,4,2,4,4,5,0,5,3,5,4,6,1,6,2,0xFF under_char_offsets: defb 7,0,7,1,7,2,7,3,7,4,0xFF accent_char_offsets: defb 0,2,1,3,0xFF L_curly_char_offsets: defb 0,3,1,2,2,2,3,1,4,2,5,2,6,3,0xFF R_curly_char_offsets: defb 0,1,1,2,2,2,3,3,4,2,5,2,6,1,0xFF pipe_char_offsets: defb 0,2,1,2,2,2,4,2,5,2,6,2,0xFF tilde_char_offsets: defb 2,1,3,0,3,2,3,4,4,3,0xFF carat_char_offsets: defb 0,2,1,1,1,3,3,0,3,4,0xFF block_char_offsets: defb 0,0,0,1,0,2,0,3,0,4,1,0,1,1,1,2,1,3,1,4,2,0,2,1,2,2,2,3,2,4,3,0,3,1,3,2,3,3,3,4 defb 4,0,4,1,4,2,4,3,4,4,5,0,5,1,5,2,5,3,5,4,6,0,6,1,6,2,6,3,6,4,7,0,7,1,7,2,7,3,7,4 defb 8,0,8,1,8,2,8,3,8,4,0xFF square_char_offsets: defb 0,0,0,1,0,2,0,3,0,4,1,0,1,4,2,0,2,4,3,0,3,4,4,0,4,4,5,0,5,4,6,0,6,1,6,2,6,3,6,4,0xFF dot_table: defb 128,64,32,16,8,4,2,1 tiny_basic: ds 2043,0 ;put Tiny BASIC binary code here org 2000h ;start of RAM in system with 8K ROM ;Variables in RAM dot_count: defb 0 test_char: defb 0 test_line: defb 0 test_pos: defb 0 char: defb 0 char_line: defb 0 char_pos: defb 0 counter: defb 0 anchor_dot_x: defw 0 anchor_dot_y: defb 0 dot_x: defw 0 dot_y: defb 0 clear_flag: defb 0 current_location: defw 0 line_count: defb 0 byte_count: defb 0 value_pointer: defw 0 current_value: defw 0 string_pointer: defw 0 buffer_pointer: defw 0 get_line_char: defb 0 help_table_pointer: defw 0 help_string_pointer: defw 0 shift_down: db 0 ctrl_down: db 0 load_length: defw 0 load_address: defw 0 bdump_address: defw 0 bdump_length: defw 0 timer_tick: defb 0 blink_flag: defb 0 capslock: defb 0 buffer: ds 128,0 ;buffer in RAM line_offset_table: ds 480,0