; **************************************************************************** ; * Assembly File Listing to generate 4K "ROM A" for Galaksija microcomputer * ; **************************************************************************** ; Introduction ; ************ ; Disassembled from a binary ROM image and annotated by ; Tomaz Solc (tomaz.solc@tablix.org) ; using z80dasm (http://www.tablix.org/~avian/blog/articles/z80dasm) ; between September 2006 and September 2007 ; The original assembly listing, complete with its lack of useful comments ; is available at: ; http://www.galaksija.org/hr/index.php?title=Disasemblirani_ROM_A ; (Titled "MALI RACUNAR GALAKSIJA" by Voja Antonic 03.01.1984) ; $Id: rom1.asm,v 1.26 2007-09-23 12:06:17 avian Exp $ ; "ROM A" contains basic operating system of Galaksija, which can be roughly ; separated into following sections: ; o Initialization routines, ; o video driver, ; o keyboard scanning and simple terminal emulation, ; o BASIC interpreter, ; o RPN FP calculator and ; o audio cassette load/save routines. ; This ROM is a work of art when you consider how tighly optimized for size ; it is and a nightmare if you wish to completely understand or even modify ; anything in it. ; This disassembly is a work in progress. Sections of the code remain ; uncommented, however this file should assemble into a binary that is ; identical to the original ROM image. ; Any contributions (for example additional comments on the code, patches, ; etc.) are most welcome at tomaz.solc@tablix.org. ; Code sightseeing ; **************** ; A must-see list of 10 most-interesting parts of code: ; l0098h Part of video driver code used as ASCII string for ; BASIC interpreter. ; l00a0h Part of video driver code used a 1.0 floating point ; constant. ; l0038h Video driver code as a whole, tuned to one T cycle accuracy. ; l038ch A tutorial on how to use "ld" instructions instead of "jr" ; l0393h and save a few bytes in the process. ; l0396h ; l0018h A function that gets its arguments from bytes in ROM that ; follow its call. ; l0d2ah Increasing randomness of pseudo-random generator by changing ; the seed after some keys are pressed. ; l0d70h One look-up table nested inside unused space of another. ; l06cfh DOT - a true multi-purpose function: query, set or reset a ; pseudo-graphic pixel or turn real time clock on or off ; all in one block of code. ; l0e58h Record one additional garbage byte at the end of an audio tape ; because it saves one "jr" instruction in ROM. ; l0fffh One spare byte for future expansions. ; About the latch ; *************** ; A 6-bit register (called "latch" in the original documentation) can be ; accessed on all memory addresses that can be written in binary as ; 0 0 1 0 0 x x x x x 1 1 1 x x x ; (for example 207fh as used in VIDEO_INT) ; The content is write-only. A read from these addresses will return an ; unspecified value. ; Individual bits have the following meaning: ; ; 7 Clamp RAM A7 to one (1 disabled, 0 enabled) ; --- ; 6 Cassette port output bit 0 ; --- ; 5 Character generator row bit 3 ; --- ; 4 Character generator row bit 2 ; --- ; 3 Character generator row bit 1 ; --- ; 2 Character generator row bit 0 ; Cassette port output bit 1 ; --- ; 1 Unused ; --- ; 0 Unused ; Character generator row bits hold the current row of the character being ; drawn to the screen. ; Cassette port is high if both output bits are 1, low if both are 0 and ; zero if one bit is 1 and one is 0. ; Bit 7 forces RAM address line A7 to one. This is required because the top ; bit of the R register never changes (only bottom 7 bits are incremented ; during each opcode). ; ROM A is mapped to memory addresses from 0000h to 0fffh org 00000h ; 'START' ; ======= ; At power on Z80 starts to execute code here. Interrupt mode is set to 0. ;; START l0000h: di ;0000 Disable interrupts sub a ;0001 Clear accumulator jp l03dah ;0002 Jump forward to START_2, the remainder of ; the initialization code. ; 'EVALUATE NEXT INTEGER ARGUMENT' ; ================================ ; Evaluate an integer expression at DE behind a ASCII comma. If comma is not ; found, jump to WHAT_RST. ;; EVAL_INT_EXP_NEXT l0005h: rst 18h ;0005 Call READ_PAR db ',' ;0006 db l0034h-$-1 ;0007 Jump to WHAT_RST if there is no next argument ; at DE. ; 'EVALUATE INTEGER EXPRESSION' ; ============================= ; Evaluate an integer expression at DE and return the result in HL. ;; EVAL_INT_EXP l0008h: call l0ab2h ;0008 Call EVAL_EXPRESSION jp l0a6dh ;000b Jump to FP_TO_INT and return. ; 'FAST MODE' ; =========== ; Disable video interrupt. ;; FAST l000eh: di ;000e ret ;000f ; 'COMPARE HL WITH DE' ; ==================== ; This function compares HL with DE register pair and sets C and Z flags ; accordingly ; Parameters: ; DE, HL: values to be compared ; Returns: ; C, Z flag ; Destroys: ; A, all other flags ;; CMP_HL_DE l0010h: ld a,h ;0010 Load H into A cp d ;0011 Compare with D ret nz ;0012 Return if not equal ld a,l ;0013 Load L into A cp e ;0014 Compare with E ret ;0015 Return ; 'SLOW MODE' ; =========== ; Enable video interrupt. ;; SLOW l0016h: ei ;0016 ret ;0017 ; 'READ PARAMETER' ; ================ ; This function reads the byte following the calling "rst" instruction and ; returns to a different location (specified with the second following byte) ; if the byte doesn't match the first non-space character in the string pointed ; to by DE register. ; Example: ; rst 18h ; 0005 ; db ',' ; 0006 ; db 02ch ; 0007 ; ... ; 0008 ; This compares the first non-space character in DE with ASCII comma ',' ; ignoring any leading space. If it matches, READ PARAMETER returns to the next ; instruction following the two bytes (at 0008h). If not, it adds 02ch to this ; return address (in this case it would return to 0034h). In the first case ; DE is also incremented to account for the matched byte. ; In pseudo-code: ; ; If (**SP) == (EAT_SPACE(DE)) Then ; *SP=*SP+2 ; DE=DE+1 ; Else ; *SP=*SP+2+(*(*SP+1)) ; End ; Parameters: ; DE: Pointer a string. ; Returns: ; DE: Skips leading space and increments DE, if there is a match. ; A: First non-space character in DE. ; Zf: Set if character matches, reset if not. ; Destroys: ; flags. ;; READ_PAR l0018h: ex (sp),hl ;0018 Load a pointer from top of stack into HL call l0105h ;0019 Call EAT_SPACE cp (hl) ;001c Compare memory pointed by HL with A jp l0194h ;001d Jump to the rest of the function at READ_PAR_2 ; 'PUT CHARACTER ON SCREEN' ; ========================= ; Print a character in A to the screen at the current cursor position ; (CURSOR_POS) ; Parameters: ; A: Character to print ; Returns: ; HL': Position of the printed character + 1 ; Destroys: ; flags ;; PUTCH_RST l0020h: exx ;0020 Save BC, DE and HL registers cp 020h ;0021 Set C flag if character is ASCII special. ; (ASCII code less than 20h) call l09b5h ;0023 Jump to remainder of function. exx ;0026 Restore BC, DE and HL registers. ret ;0027 Return ; 'CLEAR HL' ; ========== ; Loads 0000h into HL ;; CLEAR_HL l0028h: ld hl,l0000h ;0028 Load 0000h into HL ret ;002b Return ; 'ONE OVER TEN FLOATING POINT CONSTANT' ; ====================================== ; Approximately 0.1 in four-byte floating point format. ;; FP_ONE_OVER_TEN l002ch: db 0cch ;002c Mantissa: cccccch db 0cch ;002d db 0cch ;002e Sign: 00h db 07eh ;002f Exponent: fdh ; 'NEXT BASIC STATEMENT' ; ====================== ; Continue execution of the next BASIC statement. BASIC commands call this ; function at the end. It never returns. ;; BASIC_NEXT l0030h: pop af ;0030 Remove return address from stack call l0414h ;0031 Call CONTINUE l0034h: jp l078fh ;0034 Jump to WHAT_RST if CONTINUE found no ; valid next command. ; 'ROM A VERSION' ; =============== ;; ROM_VERSION db 28 ;0037 ; 'VIDEO INTERRUPT DRIVER' ; ======================== ; This routine gets called 50 times per second via the Z80's maskable ; interrupt. ; Interrupt gets triggered on the vertical sync impulse. Video synchronization ; hardware then makes sure (by inserting WAIT states) that the first opcode ; starts to execute exactly in sync with the next horizontal sync. ; Timing is very important here! This routine must execute in perfect sync ; with the video hardware. ; The routine must make sure that the R and I registers are properly set at ; exactly the right time so that the Z80's memory refresh feature can start ; reading video line data from video RAM. It also must latch the correct video ; line number into the register connected to the character generator ROM and ; adjust the RAM memory map if needed (A7 line). ; The address of a character on screen: ; ; A7 from latch ; | ; <-- I register | | | R register --> ; || || v | || ; Address bits || 11 | 10 | 9 | 8 || 7 | 6 | 5 | 4 || 3 | 2 | 1 | 0 ; || | || | || ; | | ; <-- Base address (2800h) | Row (4 bits) | Column (5 bits) ;; VIDEO_INT T states l0038h: push af ;0038 11 Save all used registers on the stack. push bc ;0039 11 push de ;003a 11 push hl ;003b 11 ld hl,02bb0h ;003c 10 Load SCROLL_CNT address to HL ld a,0c0h ;003f 7 Load A with 192 sub (hl) ;0041 7 sub (hl) ;0042 7 sub (hl) ;0043 7 Subtract 3 * SCROLL_CNT from A ld e,a ;0044 4 Load result into E ld a,(hl) ;0045 7 Load A with value from SCROLL_CNT rrca ;0046 4 Rotate right A three times rrca ;0047 4 rrca ;0048 4 ld b,a ;0049 4 Load result into B or a ;004a 4 set Z flag if A = 0 ; reset C flag ; ----- ; 113 ; The following code is a pause with length (24 + B * 18) T states. ; This determines the vertical position of the screen. jr z,l004dh ;004b 12/7 Wait 5 T states if Z set... l004dh: jr z,l0054h ;004d 12/7 and jump forward dec (hl) ;004f 11 ... else decrement SCROLL_CNT xor a ;0050 4 clear A (reset C flag) l0051h: ret c ;0051 5 wait 5 T states ; (C flag always reset) djnz l0051h ;0052 13/8 ... loop B times l0054h: inc hl ;0054 6 Load SCROLL_FLAG address to HL ld (hl),a ;0055 13 Set SCROLL_FLAG = 0 ld b,e ;0056 4 Set B = 192 - 3 * SCROLL_CNT ld hl,0207fh ;0057 10 Load latch address to HL ld c,l ;005a 4 Load C with 7fh ld a,(02ba8h) ;005b 13 Load A with HORIZ_POS rra ;005e 4 Divide A with 2 (C flag always reset) ; load bit 0 into C flag. ; ---- ; 54 ; B holds the total number of scanlines to be drawn. ; C holds the current line number ; During one iteration of VIDEO_DISPLAY_LOOP one line of characters is drawn. ; Here is another pause with length (2 + 5 * Cf + 16 * A) T states. ; This determines the horizontal position of the screen. ; Note: HORIZ_POS must be >= 2. ; On later iterations of VIDEO_DISPLAY_LOOP A is always set to 3, this means ; a constant pause of 43 T states jr c,l0061h ;005f 12/7 Wait 5 T if HORIZ_POS odd. ;; VIDEO_DISPLAY_LOOP l0061h: dec a ;0061 4 Decrement A jr nz,l0061h ;0062 12/7 ...loop A times jr l006dh ;0064 12 Jump forward ; 'HARD BREAK' ; ============ ; On a non-maskable interrupt, Z80 jumps here. This is used as a "hard break" ; button on Galaksija. ;; HARD_BREAK di ;0066 Disable interrupts. ; 'RESET BASIC' ; ============= ; Resets BASIC interpreter. ;; RESET_BASIC l0067h: ld sp,02ba8h ;0067 Load HORIZ_POS address to SP jp l0317h ;006a Jump to the rest at RESET_BASIC_2 ; Continuing VIDEO_DISPLAY_LOOP l006dh: inc c ;006d 4 Increment C ld a,c ;006e 4 Load A with C and 008h ;006f 7 Set A = A & 08h rrca ;0071 4 Rotate A right 3 times rrca ;0072 4 rrca ;0073 4 or 028h ;0074 7 Set A = A | 28h ld i,a ;0076 9 Load I with result ; I now has the upper 8 bits of the ; address for the line that will ; be drawn to the sceeen (either 28h ; or 29h) inc de ;0078 6 NOP ld de,0080ch ;0079 10 Load DE with 080ch ld a,c ;007c 4 Load A with C rrca ;007d 4 Rotate A right 3 times rrca ;007e 4 rrca ;007f 4 ccf ;0080 4 Complement C flag rr d ;0081 8 Rotate D right through C flag ; D is now 84h or 04h ; The MSB determines whether the RAM ; address line A7 will be forced to 1 or 01fh ;0083 7 Set A = A | 1fh rlca ;0085 4 Rotate left sub 040h ;0086 7 Subtract 40h rrca ;0088 4 Rotate right ld r,a ;0089 9 Load R with A ; E holds the number of scan lines to be drawn for this line of characters. ; During one iteration of VIDEO_LINE_LOOP one scanline of video is drawn. ;; VIDEO_LINE_LOOP l008bh: ld (hl),d ;008b 7 Latch value in D to the character ; generator register ; Opcodes are arbitrary, but must all consist of 4 T states (one M state) - ; 8 pixels are drawn during each opcode. inc d ;008c 4 inc d ;008d 4 inc d ;008e 4 inc d ;008f 4 xor a ;0090 4 scf ;0091 4 rra ;0092 4 rra ;0093 4 xor d ;0094 4 ld d,a ;0095 4 ld h,c ;0096 4 ld a,b ;0097 4 ;; BREAK_STRING l0098h: ; This part of the line drawing code also serves as a string "BREAK" for the ; basic interepreter. ld b,d ;0098 4 B ld d,d ;0099 4 R ld b,l ;009a 4 E ld b,c ;009b 4 A ld c,e ;009c 4 K nop ;009d 4 \0 ld b,a ;009e 4 ld c,h ;009f 4 ; This part of the line drawing code also serves as a constant 1.0 in four-byte ; floating point format. ;; FP_ONE l00a0h: nop ;00a0 4 00h Mantissa: 800000h nop ;00a1 4 00h add a,b ;00a2 4 80h Sign bit: 00h nop ;00a3 4 00h Exponent: 01h xor a ;00a4 4 scf ;00a5 4 rra ;00a6 4 rra ;00a7 4 rra ;00a8 4 ld h,a ;00a9 4 rla ;00aa 4 ; At the end of line drawing: ; A = 40h ; B remains unmodified ; C remains unmodified ; D = 40h XOR (D + 4) ; E remains unmodified ; HL remains unmodified (207fh) ld (hl),a ;00ab 10 During the last character we clear ; the register so nothing gets ; drawn outside the screen (character ; generator line 0) ; End of line drawing dec b ;00ac 4 Decrement B jr z,l00beh ;00ad 12/7 Break from loop if B is zero. ld a,r ;00af 9 Load A with R sub 027h ;00b1 7 Subtract 27h and l ;00b3 4 Clear the top bit (L = 7fh) ; This bit is provided by A7 clamp ld r,a ;00b4 9 Load R with A dec e ;00b6 4 Decrement E jp nz,l008bh ;00b7 12/7 If not zero jump to VIDEO_LINE_LOOP ld a,003h ;00ba 7 Load A with 03h jr l0061h ;00bc 12 Jump to VIDEO_DISPLAY_LOOP ; At this point the screen has been drawn. Timing is no longer critical. l00beh: ld (hl),0bch ;00be Latch bch into the register ; This disables A7 clamp and sets character ; generator to an empty line 15 so that nothing ; gets drawn outside the screen ; The following code increments the real-time clock in BASIC string $Y. ; The clock only works correctly if Y$ contains one of the following ; ASCII combinations: ; Y$ = "HH:MM:SS:PP\0", Y$ = "HH:MM:SS\0PP", Y$ = "HH:MM\0SS:PP" ; Where HH are hours, MM minutes, SS seconds and PP 1/100th of a second ld a,(02a82h) ;00c0 Load memory location 2a82h into A ; This is third character in Y$ cp 03ah ;00c3 Compare A with 3ah (ASCII ':') jr nz,l00fbh ;00c5 Jump to VIDEO_END if not equal ld a,(02bafh) ;00c7 Load CLOCK_ON into A rlca ;00ca Move MSB into C flag jr nc,l00fbh ;00cb Jump to VIDEO_END if MSB is zero ; Clock is enabled and seems to contain a valid ASCII string. ; HL contains pointer to the current character ; B contains number of characters left ld hl,02a8ah ;00cd Load 2a8ah into HL ; This is the last (11th) character in string Y$ ld de,03930h ;00d0 Load 3930h into DE ; D = '9' E = '0' ld b,008h ;00d3 Load 08h into B ld a,d ;00d5 Load 39h into A ; A = '9' inc (hl) ;00d6 Increment last character (1/100th s) two times inc (hl) ;00d7 (this routine is called 50 times per second) cp (hl) ;00d8 Compare last character with '9' jr l00dfh ;00d9 Jump forward ; This code propagates change in the 1/100th field to seconds, minutes and ; hours l00dbh: inc (hl) ;00db Increment character cp (hl) ;00dc Compare character with A ld a,035h ;00dd Load '5' into A l00dfh: jr nc,l00ebh ;00df If character less or equal to A ('9' or '5') ; then jump forward. ld (hl),e ;00e1 Replace current character with '0' dec hl ;00e2 Move pointer to the next character. bit 0,b ;00e3 If B even... jr z,l00e9h ;00e5 ...jump forward dec hl ;00e7 Else move pointer again (to skip ':' or '\0') ld a,d ;00e8 Load '9' into A l00e9h: djnz l00dbh ;00e9 Loop until B equals 0 l00ebh: dec b ;00eb Decrement B djnz l00fbh ;00ec Jump to VIDEO_END if B not equal to 0... ; ...else hours field was updated and must ; be checkd for overflow ld a,(hl) ;00ee Load the character into A cp 034h ;00ef Compare with '4' jr c,l00fbh ;00f1 If character less or equal to '4' jump to ; VIDEO_END dec hl ;00f3 Move pointer to the next character bit 1,(hl) ;00f4 Test bit 1 (character equal to '2' or more) jr z,l00fbh ;00f6 If bit 1 not set jump to VIDEO_END... ld (hl),e ;00f8 Else load '0' in the last two characters inc hl ;00f9 ld (hl),e ;00fa ;; VIDEO_END l00fbh: jp (iy) ;00fb Jump to video hook (jumps to l00fdh by ; default) l00fdh: pop hl ;00fd Restore all registers from stack pop de ;00fe pop bc ;00ff pop af ;0100 ei ;0101 Enable interrupts reti ;0102 Return from interrupt ; 'EAT SPACE' ; =========== ; Increments DE until it points to a non-ASCII space (20h) character ; Parameters: ; DE: starting position ; Returns: ; A: first non-space character ; DE: address of the character in A ; Destroys: ; flags l0104h: inc de ;0104 Increment DE ;; EAT_SPACE l0105h: ld a,(de) ;0105 Load memory byte pointed by DE into A cp 020h ;0106 Compare A with ASCII ' ' jr z,l0104h ;0108 Loop if equal... ret ;010a ...else Return ; 'ARR$' ; ====== ;; ARR$ BASIC command ;; ARR$ l010bh: inc hl ;010b 23 # xor a ;010c af call l0df3h ;010d cd f3 0d ld (02a99h),hl ;0110 22 99 2a " * rst 30h ;0113 f7 l0114h: rst 8 ;0114 cf inc hl ;0115 23 # xor a ;0116 af call l0df3h ;0117 cd f3 0d push de ;011a d5 ld de,(02a99h) ;011b ed 5b 99 2a [ * inc de ;011f 13 rst 10h ;0120 d7 jr nc,l0154h ;0121 30 31 0 1 jr l0146h ;0123 18 21 ! ; 'LOCATE BASIC NUMERIC VARIABLE' ; =============================== ; Locates a BASIC variable containing 4-byte floating point number and stores ; its address in HL. ; Recognizes ordinary variables from A - Z and A(I) array. ; Returns: ; HL: Location of the variable ;; LOCATE_VARIABLE l0125h: call l0105h ;0125 Fetch the next character (call EAT_SPACE) ; Return with Cf set if character not between 'A' and 'Z' sub 041h ;0128 Subtract ASCII value 'A' ret c ;012a Return if less cp 01ah ;012b Compare character with ASCII value 'Z' + 1 ccf ;012d ret c ;012e Return if greater or equal inc de ;012f Move to the next character and a ;0130 jr nz,l015eh ;0131 Jump to LOCATE_VARIABLE_BZ if character ; is not A rst 18h ;0133 Call READ_PAR db '(' ;0134 db l015dh-$-1 ;0135 Jump to LOCATE_VARIABLE_A if character A is ; not followed by '(' ; A(I) array rst 8 ;0136 HL = index (call EVAL_INT_EXP) inc hl ;0137 Increment index add hl,hl ;0138 HL = index * 4 add hl,hl ;0139 push de ;013a Save DE on stack jr c,l0154h ;013b Jump to SORRY_RST on carry (index too large) ld de,(02a99h) ;013d HL = index * 4 + ARRAY_LEN add hl,de ;0141 pop de ;0142 Fetch DE from stack push de ;0143 jr c,l0153h ;0144 Jump to SORRY_RST_PUSH_DE on carry ; (index too large) l0146h: pop de ;0146 Restore DE rst 18h ;0147 Call READ_PAR db ')' ;0148 db l0153h-$-1 ;0149 Jump to SORRY_RST_PUSH_DE if no closing ')' ; found. push de ;014a Save DE ex de,hl ;014b DE = index * 4 + ARRAY_LEN call l0183h ;014c Call FREE_MEM rst 10h ;014f Call CMP_HL_DE jr nc,l0188h ;0150 Jump to SUB_RAMTOP_HL if enough memory and ; return db 03eh ;0152 Dummy ld a,nn (ignore push de command) ; '"SORRY" ERROR RESTART' ; ======================= ; This restart is called when there is not enough memory available to ; complete a function or command. ;; SORRY_RST_PUSH_DE l0153h: push de ;0153 Push current parser address to stack. ;; SORRY_RST l0154h: call l0799h ;0154 Call BASIC_ERROR dm "SORRY", 00dh ;0157 ; Ordinary variable ;; LOCATE_VARIABLE_A l015dh: xor a ;015d Clear A ;; LOCATE_VARIABLE_BZ l015eh: ld h,02ah ;015e HL = 2a00h + A * 4 rla ;0160 rla ;0161 ld l,a ;0162 xor a ;0163 Clear A and Cf ret ;0164 Return ; 'CONVERT ASCII HEX CHAR TO AN INTEGER' ; ====================================== ; This function converts a single ASCII character to its hexadecimal numerical ; value (for example 'B' returns 11) and increments DE. ; Parameters: ; DE: Pointer to the character to be converted ; Returns: ; A: Numerical value of character in (DE) or the character in (DE) on ; error (character not between ASCII '0' and '9'). ; DE: Incremented by 1 or unmodified on error. ; C flag: Set on error, reset otherwise ; Destroys: ; Flags ;; HEXCHAR_TO_INT l0165h: call l0172h ;0165 First try to convert an ASCII integer ret nc ;0168 return if call to CHAR_TO_INT succeeded cp 041h ;0169 Return with Cf set if character less than 'A' ret c ;016b add a,009h ;016c Add 09h to ASCII code (lower 4 bits are now ; equal to the hex value) cp 050h ;016e Compare with 'F' + 09h + 1 jr l0178h ;0170 Jump forward (check for error, clear upper ; for bits and return) ; 'CONVERT ASCII CHAR TO AN INTEGER' ; ================================== ; This function converts a single ASCII character to its decimal numerical ; value and increments DE. ; Parameters: ; DE: Pointer to the character to be converted ; Returns: ; A: Numerical value of character in (DE) or the character in (DE) on ; error (character not between ASCII '0' and '9'). ; DE: Incremented by 1 or unmodified on error. ; C flag: Set on error, reset otherwise ; Destroys: ; Flags ;; CHAR_TO_INT l0172h: ld a,(de) ;0172 Load (DE) into A cp 030h ;0173 Compare with 30h (ASCII '0')... ret c ;0175 ...and return if smaller. cp 03ah ;0176 Compare with 3ah (ASCII '9' + 1)... l0178h: ccf ;0178 (complement carry flag) ret c ;0179 ...and return if larger or equal. inc de ;017a Increment DE l017bh: and 00fh ;017b A = A & 0fh (clear upper 4 bits) ; also resets carry flag ret ;017d Return l017eh: ld (hl),a ;017e 77 w l017fh: inc hl ;017f 23 # ld a,l ;0180 7d } jr l017bh ;0181 18 f8 ; 'FREE MEMORY REMAINING' ; ======================= ; Calculate bytes available from end of BASIC program to top of RAM ; Returns: ; HL: Free bytes available. ;; FREE_MEM l0183h: push de ;0183 Save DE to stack ld de,(02c38h) ;0184 Load BASIC_END into DE ; 'SUBTRACT HL FROM RAM_TOP' ; ========================== ; Returns: ; HL: HL = RAM_TOP - HL ;; SUB_RAMTOP_HL l0188h: ld hl,(02a6ah) ;0188 Load RAM_TOP into DE ld a,l ;018b HL = HL & 0fff0h and 0f0h ;018c (resets Cf) ld l,a ;018e sbc hl,de ;018f HL = HL - DE - 0 pop de ;0191 Restore DE xor a ;0192 Clear A (resets carry) ret ;0193 Return ; 'READ PARAMETER (cont.)' ; ======================== ;; READ_PAR_2 l0194h: inc hl ;0194 Increment HL jr z,l019eh ;0195 Jump forward if character in DE matches push bc ;0197 Save BC on stack ld c,(hl) ;0198 Load (HL) into BC ld b,000h ;0199 add hl,bc ;019b Add BC to HL pop bc ;019c Restore BC from stack dec de ;019d Decrement DE l019eh: inc de ;019e Increment DE inc hl ;019f Increment HL ex (sp),hl ;01a0 Store HL back to the top of the stack ret ;01a1 Return ; 'CONVERT STRING TO FLOATING POINT NUMBER' ; ========================================= ; Converts a string at DE to a floating point number and pushes it to the ; arithmetic stack. ; Returns: ; Zf: Set if no errors occured ;; STRING_TO_FP l01a2h: call l0248h ;01a2 Clear HL' C' (call CLEAR_CX_HLX_B6) ld b,000h ;01a5 Clear B and C ld c,b ;01a7 call l0105h ;01a8 Skip any leading whitespace (call EAT_SPACE) l01abh: call l01b0h ;01ab Call STRING_TO_FP_FETCH_DECIMAL jr l01abh ;01ae Loop ; L' H' C' L H ; ---------------------------------------------------------- ; 24 bit mantissa Exp Sign ; B register (holds status of the conversion) ; bit 7 6 5 4 3 2 1 0 ; ---------------------------------------------------------------------- ; Mantissa overflow ; Mantissa started with a number ; Exponent started with a number ; Negative exponent ; Decimal part ; C register holds (negative) number of decimals after the decimal point. ; Stack: Top ; -> STRING_TO_FP ;; STRING_TO_FP_FETCH_DECIMAL l01b0h: call l0172h ;01b0 Call CHAR_TO_INT jr c,l01d5h ;01b3 Jump on conversion error set 6,b ;01b5 Set bit 6 of B bit 7,b ;01b7 jr nz,l01d0h ;01b9 Jump forward on mantissa overflow. call l01c3h ;01bb Call STRING_TO_FP_ADD_DECIMAL bit 0,b ;01be ret z ;01c0 Decrement C if after decimal point. dec c ;01c1 ret ;01c2 Return ; Stack: Top ; -> STRING_TO_FP ; -> STRING_TO_FP_FETCH_DECIMAL ;; STRING_TO_FP_ADD_DECIMAL l01c3h: call l024fh ;01c3 Call FP_MUL_10_ADD_A ret z ;01c6 Return if no overflow in mantissa ; Restore original mantissa if there was an overflow exx ;01c7 ld h,d ;01c8 Load DE' into HL' ld l,e ;01c9 ex af,af' ;01ca ld c,a ;01cb Load A' into C' exx ;01cc set 7,b ;01cd Set bit 7 of B pop af ;01cf Remove top return address from stack. ; (will return to STRING_TO_FP) l01d0h: bit 0,b ;01d0 ret nz ;01d2 Increment C if before decimal point. inc c ;01d3 ret ;01d4 Return ; Stack: Top ; -> STRING_TO_FP l01d5h: rst 18h ;01d5 Call READ_PAR db '.' ;01d6 db l01ddh-$-1 ;01d7 ; Decimal point found bit 0,b ;01d8 set 0,b ;01da ret z ;01dc Return if bit 0 not set, ; else set bit 0 of B. ; Two decimal points found l01ddh: pop af ;01dd Remove top return address from stack. bit 6,b ;01de ret z ;01e0 Return from STRING_TO_FP if first character ; of mantissa was not a number. ld hl,l0018h ;01e1 Exponent = 24, sign = positive push bc ;01e4 Save BC on stack push de ;01e5 Save DE on stack exx ;01e6 call l0914h ;01e7 Call CORRECT_EXP_ADD_IX_10 pop de ;01ea Restore DE from stack ld bc,l01f3h ;01eb Push STRING_TO_FP_PARSE_EXP address on stack. push bc ;01ee push de ;01ef Save DE on stack jp l0b6dh ;01f0 Jump to STORE_ARITHM_RET and return ; to STRING_TO_FP_PARSE_EXP ; At this point mantissa is on the top of arithmetic stack and DE points ; to the beginning of the exponent. ; Stack: Top ; -> STRING_TO_FP ;; STRING_TO_FP_PARSE_EXP l01f3h: pop bc ;01f3 Restore BC from stack push de ;01f4 Save DE on stack rst 18h ;01f5 Call READ_PAR db 'E' ;01f6 db l0213h-$-1 ;01f7 No exponent rst 18h ;01f8 Call READ_PAR db '+' ;01f9 db l01fdh-$-1 ;01fa Ignore leading + sign jr l0202h ;01fb l01fdh: rst 18h ;01fd Call READ_PAR db '-' ;01fe db l0202h-$-1 ;01ff set 1,b ;0200 Mark negative exponent ;; STRING_TO_FP_GET_EXP l0202h: call l024ah ;0202 Call CLEAR_CX_HLX l0205h: call l0172h ;0205 Call CHAR_TO_INT jr c,l0217h ;0208 Break from loop on conversion error set 5,b ;020a Mark exponent started with number call l024fh ;020c Add decimal to the exponent ; (call FP_MUL_10_ADD_A) jr nz,l0225h ;020f Jump to HOW_RST_PUSH_DE on exponent overflow jr l0205h ;0211 Loop l0213h: pop de ;0213 Restore DE xor a ;0214 Clear A jr l022eh ;0215 l0217h: bit 5,b ;0217 jr z,l0213h ;0219 Jump to FIXME if exponent did not start with ; a number pop af ;021b FIXME exx ;021c ld a,c ;021d or h ;021e Test C' and H' ld a,l ;021f A = L' exx ;0220 jr nz,l0225h ;0221 If C' or H' not equal to zero, call HOW_RST ; (exponent overflowed) bit 7,a ;0223 l0225h: jp nz,l065ah ;0225 If mantissa overflowed, call HOW_RST bit 1,b ;0228 If exponent negative, negate A jr z,l022eh ;022a neg ;022c l022eh: add a,c ;022e Add exponent and number of decimal points ; Mantissa is still on the top of the arithmetic stack. Exponent is in A. ; Multiply mantissa with 10^A. ;; STRING_TO_FP_EXP_LOOP l022fh: and a ;022f jr z,l0245h ;0230 If A zero, jump to STRING_TO_FP_END bit 7,a ;0232 jr z,l023dh ;0234 Multiply by 10 if exponent positive, ; else divide by 10 inc a ;0236 Increment A push af ;0237 Save A on stack call l0af4h ;0238 Call FP_DIV_10 jr l0242h ;023b l023dh: dec a ;023d Decrement A push af ;023e Save A on stack call l0ae3h ;023f Call FP_MUL_10 l0242h: pop af ;0242 Restore A from stack jr l022fh ;0243 Jump to STRING_TO_FP_EXP_LOOP ;; STRING_TO_FP_END l0245h: bit 6,b ;0245 Test bit 6 of B (valid mantissa) ret ;0247 Return ; 'CLEAR C' HL'' ; ============== ; Loads 00 0000h into the mantissa in C' HL' and clears bit 6 of B ;; CLEAR_CX_HLX_B6 l0248h: res 6,b ;0248 Clear bit 6 of B ;; CLEAR_CX_HLX l024ah: exx ;024a Exchange BC,DE,HL with BC',DE',HL' rst 28h ;024b Call CLEAR_HL ld c,l ;024c Load 00h into C exx ;024d Exchange BC,DE,HL with BC',DE',HL' ret ;024e Return ; 'ADD A DECIMAL NUMBER TO MANTISSA' ; ================================== ; Multiplies floating point number's mantissa in HL' C' by 10 and adds integer ; in A to it. ; Parameters: ; HL' C': Input mantissa (24 bit) ; A: Integer to add ; Returns: ; HL' BC': Output mantissa (32 bit) ; DE' A': Unmodified input mantissa (24 bit) ; Zf: Set if B' is zero ;; FP_MUL_10_ADD_A l024fh: ex af,af' ;024f exx ;0250 ld d,h ;0251 DE' A' = HL' C' ld e,l ;0252 ld a,c ;0253 ld b,000h ;0254 Clear B' push af ;0256 Push AF' on stack ; L' H' C' B' L H ; ---------------------------------------------------------- ; 32 bit mantissa (MSB) Exp Sign add hl,hl ;0257 HL' BC' = HL' BC' * 2 rl c ;0258 rl b ;025a add hl,hl ;025c HL' C' = HL' C' * 2 rl c ;025d rl b ;025f add hl,de ;0261 HL' BC' = HL' BC' + DE' A' adc a,c ;0262 ld c,a ;0263 ld a,000h ;0264 adc a,b ;0266 ld b,a ;0267 ; We now have HL' BC' = HL' BC' * 5 pop af ;0268 Pop AF' from stack push de ;0269 Push DE' to stack ld d,000h ;026a Clear D' add hl,hl ;026c HL' BC' = HL' BC' * 2 rl c ;026d rl b ;026f ; We now have HL' BC' = HL' BC' * 10 ex af,af' ;0271 Exchange AF with AF' ld e,a ;0272 Load A into E' add hl,de ;0273 HL' BC' = HL' BC' + E' ld a,d ;0274 adc a,c ;0275 ld c,a ;0276 ld a,d ;0277 adc a,b ;0278 ld b,a ;0279 pop de ;027a Pop DE' from stack exx ;027b ret ;027c Return ; 'EDIT' ; ====== ; "EDIT" BASIC command. ; Edit a line of BASIC with a simple line editor. Takes one integer argument ; which is the line number to edit. ; Delete key was pressed while in line editor mode. Remove one character ; at '_' cursor position. ;; EDIT_DELETE l027dh: ld e,l ;027d DE' = HL' ld d,h ;027e l027fh: inc de ;027f (DE') = (DE' + 1) ld a,(de) ;0280 dec de ;0281 ld (de),a ;0282 inc de ;0283 Increment DE' cp 00dh ;0284 Loop until end of line is reached jr nz,l027fh ;0286 jr l02afh ;0288 Continue with EDIT_LOOP ;; EDIT_LEFT l028ah: ld a,l ;028a Check if cursor is at the beginning of buffer. cp 0b6h ;028b jr z,l02afh ;028d dec hl ;028f Move cursor one position left jr l02afh ;0290 Continue with EDIT_LOOP ;; EDIT_RIGHT l0292h: ld a,(hl) ;0292 Check if cursor is at the end of buffer. cp 00dh ;0293 jr z,l02afh ;0295 jr l02eah ;0297 Move cursor one position right ; and continue with EDIT_LOOP ; Entry point to the function ;; EDIT l0299h: call l0cd3h ;0299 HL = line number to edit or 0 on error ; (call STRING_TO_INT) call l07f2h ;029c DE = start of the BASIC line ; (call FIND_LINE) jp c,l065ah ;029f Jump to HOW_RST_PUSH_DE if exact line number ; in argument was not found. ld a,00ch ;02a2 Clear screen with PUTCH_RST rst 20h ;02a4 (print ASCII FF) ld hl,02bb6h ;02a5 Load INPUT_BUFFER address into HL ld (02a68h),hl ;02a8 Move cursor at the beggining of the buffer ; (update CURSOR_POS) call l0931h ;02ab Print the selected line into buffer ; (call PRINT_BASIC_LINE) ; HL' = address of the last character ; in buffer + 1 exx ;02ae ;; EDIT_LOOP l02afh: ld de,02800h ;02af Set CURSOR_POS to the top left corner of the ld (02a68h),de ;02b2 screen. ; Print the BASIC line in buffer on the screen with the '_' cursor inserted ; at HL'. ld de,02bb6h ;02b6 DE' = INPUT_BUFFER ld c,(hl) ;02b9 Save character at HL' in C' ld (hl),000h ;02ba and overwrite it with ASCII NUL call l0937h ;02bc Call PRINTSTR ; DE' = address of the first character after ; inserted ASCII NUL ld a,05fh ;02bf Load cursor character (ASCII '_') into A call l07b6h ;02c1 Decrement DE' ; Restore character in C' to HL' ; Call PUTCH_PRINTSTR ; DE' = address of the last character in the ; buffer. call l0cf5h ;02c4 Wait for a keypress (call KEY) cp 00dh ;02c7 "RETURN" pressed... jr z,l033ch ;02c9 ...call BASIC_CMDLINE_ENTRY and proceed as if ; the buffer was entered on the command line. or a ;02cb "DELETE" pressed... jr z,l027dh ;02cc ...jump to EDIT_DELETE cp 01dh ;02ce "LEFT" pressed... jr z,l028ah ;02d0 ...jump to EDIT_LEFT cp 01eh ;02d2 "RIGHT" pressed... jr z,l0292h ;02d4 ...jump to EDIT_RIGHT jr c,l02afh ;02d6 Ignore any other special keys (scancode < 1eh) ; (loop to EDIT_LOOP) ; Alfanumeric key was pressed... ld b,a ;02d8 Load ASCII code into B push hl ;02d9 Save '_' cursor address on stack ld hl,02c34h ;02da Check there is space in the buffer for ; another character. rst 10h ;02dd (call CMP_HL_DE) pop hl ;02de Restore '_' cursor address. jr c,l02afh ;02df Ignore key press that would overflow the ; buffer (loop to EDIT_LOOP) ; Make space for the inserted character by moving all character in front ; one position to the right. l02e1h: dec de ;02e1 (DE') = (DE' - 1) ld a,(de) ;02e2 inc de ;02e3 ld (de),a ;02e4 dec de ;02e5 Decrement DE' rst 10h ;02e6 Compare with '_' cursor position in HL' jr nz,l02e1h ;02e7 Loop until cursor position is reached ld (hl),b ;02e9 Insert ASCII code into string l02eah: inc hl ;02ea Move '_' cursor to the right. jr l02afh ;02eb Loop to EDIT_LOOP ; 'MOVE CURSOR INTO A NEW LINE' ; ============================= ; This routine prints a new line if the cursor is not in the upper left corner ; of the screen. ;; NEWLINE l02edh: ld a,(02a68h) ;02ed Set Zf if cursor in upper left corner of the and 01fh ;02f0 screen. ld a,00dh ;02f2 Load ASCII CR into A ld (02bb5h),a ;02f4 Load 0dh into FIXME ret z ;02f7 Print ASCII CR if Zf not set. rst 20h ;02f8 ret ;02f9 ; 'CHECK IF "BREAK" KEY IS PRESSED' ; ================================= ; This function checks if "BREAK" key is pressed. If so it executes the BREAK ; routine. l02fah: ld a,(02033h) ;02fa Check for "DELETE" key press. rrca ;02fd ret c ;02fe Return if key is not pressed. Else check for ; BREAK keypress again. ; FIXME: Phantom keypress recognition? ;; CHECK_BREAK l02ffh: ld a,(02031h) ;02ff Check for "BREAK" key press. rrca ;0302 jr c,l02fah ;0303 If key is pressed (LSB of A is 0), continue ; to the BREAK function. Else jump. ; 'BREAK BASIC INTERPRETER' ; ========================= ; This function gets called whenever the "BREAK" key is pressed during the ; execution of the BASIC interpreter. ;; BREAK l0305h: call l02edh ;0305 cd ed 02 ld de,l0098h ;0308 11 98 00 call l0937h ;030b cd 37 09 7 ld de,(02a9fh) ;030e ed 5b 9f 2a [ * ld a,d ;0312 7a z or e ;0313 b3 call nz,l08edh ;0314 c4 ed 08 ; 'RESET BASIC (cont.)' ; ===================== ;; RESET_BASIC_2 l0317h: ei ;0317 Enable interrupts call l02edh ;0318 Call NEWLINE ld de,l0f07h ;031b Print "READY" string call l0937h ;031e (call PRINTSTR) ;; BASIC_CMDLINE_LOOP l0321h: ; Reset BASIC interpreter variables rst 28h ;0321 Call CLEAR_HL ld de,03031h ;0322 ld sp,02aa7h ;0325 push de ;0328 Load 3031h into KEY_DIFF (2aa5-2aa6) push hl ;0329 Clear FIXME (2aa3-2aa4) push hl ;032a Clear FIXME (2aa1-2aa2) push hl ;032b Clear BASIC_LINE (2a9f-2aa0) ld hl,(02c36h) ;032c Load BASIC_START+2 into HL inc hl ;032f inc hl ;0330 push hl ;0331 Load HL into 2a9dh (FIXME) ld sp,02ba8h ;0332 Restore CPU and arithmetic stack pointers. ld ix,02aach ;0335 call l07bbh ;0339 Get command line (call GETSTR) ; Process string in input buffer and either store a BASIC line to memory or ; execute and immediate command. ;; BASIC_CMDLINE_ENTRY l033ch: push de ;033c Push address of the end of the string to stack, ld de,02bb6h ;033d Load INPUT_BUFFER address into DE call l0cd3h ;0340 Call STRING_TO_INT ; Stores line number in HL, or set Zf if ; no line number was entered. pop bc ;0343 Restore end address into BC. jp z,l038ch ;0344 No line number, jump to IMMEDIATE ; Store the entered BASIC line in memory ; Example: 20 XXX entered on the command line dec de ;0347 Store line number at (DE-2) ld a,h ;0348 (two bytes before the start of the line ld (de),a ;0349 contents) dec de ;034a ld a,l ;034b ld (de),a ;034c push bc ;034d Save line end address and line start address push de ;034e on stack. ld a,c ;034f A = C - E sub e ;0350 push af ;0351 call l07f2h ;0352 (call FIND_LINE) push de ;0355 Store start address of the next line on stack jr nz,l0368h ;0356 ; Entered line number doesn't exist yet push de ;0358 call l0811h ;0359 Move DE to the beginning of the line ; (call FIND_LINE_NEXT) pop bc ;035c BC = start address of the next line ld hl,(02c38h) ;035d 2a 38 2c * 8 , call l0944h ;0360 Call MOVE_MEM ld h,b ;0363 60 ` ld l,c ;0364 69 i ld (02c38h),hl ;0365 22 38 2c " 8 , l0368h: pop bc ;0368 c1 ld hl,(02c38h) ;0369 2a 38 2c * 8 , pop af ;036c f1 push hl ;036d e5 cp 003h ;036e fe 03 jr z,l0321h ;0370 28 af ( ld e,a ;0372 5f _ ld d,000h ;0373 16 00 add hl,de ;0375 19 ld de,(02a6ah) ;0376 ed 5b 6a 2a [ j * rst 10h ;037a d7 jp nc,00153h ;037b d2 53 01 S ld (02c38h),hl ;037e 22 38 2c " 8 , pop de ;0381 d1 call l094ch ;0382 cd 4c 09 L pop de ;0385 d1 pop hl ;0386 e1 call l0944h ;0387 cd 44 09 D jr l0321h ;038a Jump to BASIC_CMDLINE_LOOP ; 'PARSE AN IMMEDIATE COMMAND' ; ============================ ; Parses and executes a BASIC command entered on the command line. ; Calls PARSE with BASIC_CMDLINE_TABLE ;; IMMEDIATE l038ch: ld hl,l0317h ;038c Push RESET_BASIC_2 address on stack push hl ;038f ld l,(l0f0fh-1)&00ffh ;0390 HL = 0f0eh db 1 ;0392 BC = xx ; 'EVALUATE NUMERIC FUNCTIONS' ; ============================ ; Evaluates any functions from the numeric function table that appear at DE. ; Calls PARSE with BASIC_FUNC_TABLE. ;; EVAL_FUNCTIONS l0393h: ld l,(l0f9ch-1)&00ffh ;0393 HL = 0f9bh db 1 ;0395 BC = xx BC = xx l0396h: ld l,0eeh ;0396 HL = 0feeh ; 'BASIC FUNCTION PARSER' ; ======================= ; Parse BASIC commands at DE. ; Parameters: ; DE: String containing BASIC commands. ; L: Low byte of a pointer to the appropriate command table. ;; PARSE l0398h: ld h,00fh ;0398 ;; PARSE_FIRST l039ah: call l0105h ;039a Skip leading space and get first character ; from string in DE (Call EAT_SPACE) push de ;039d Store address of the first character on stack inc de ;039e Increment DE and HL inc hl ;039f cp (hl) ;03a0 Compare first character with first character ; in table jr z,l03a9h ;03a1 Jump to PARSE_COMPARE if equal. bit 7,(hl) ;03a3 Test MSB of (HL) jr nz,l03b3h ;03a5 Jump to PARSE_END if set jr l03bah ;03a7 Jump to PARSE_NEXT if characters do not match ; Compares string in DE (BASIC line) with string in HL (BASIC command table) ;; PARSE_COMPARE l03a9h: ld a,(de) ;03a9 Get next character inc de ;03aa Increment DE and HL inc hl ;03ab cp (hl) ;03ac Compare character jr z,l03a9h ;03ad Loop if they match bit 7,(hl) ;03af Test MSB of (HL) jr z,l03b6h ;03b1 Jump to PARSE_DOT if not set. ; Complete table has been checked and no matching BASIC command has been found ;; PARSE_END l03b3h: dec de ;03b3 Decrement DE jr l03c8h ;03b4 Jump to PARSE_MATCH ; A matching BASIC command has been found ;; PARSE_DOT l03b6h: cp 02eh ;03b6 Compare last character with ASCII '.' jr z,l03c3h ;03b8 If equal then jump to PARSE_MATCH_PARTIAL ; else continue checking with next command in ; the command table. ; Move (HL) to the next entry in the BASIC command table ;; PARSE_NEXT l03bah: inc hl ;03ba Increment HL until MSB of (HL) is set bit 7,(hl) ;03bb jr z,l03bah ;03bd inc hl ;03bf Increment HL pop de ;03c0 Restore DE jr l039ah ;03c1 Jump to PARSE_FIRST ;; PARSE_MATCH_PARTIAL l03c3h: inc hl ;03c3 Increment HL until MSB of (HL) is set bit 7,(hl) ;03c4 jr z,l03c3h ;03c6 ;; PARSE_MATCH l03c8h: ld a,(hl) ;03c8 HL = (HL) & 7fffh inc hl ;03c9 number in (HL) is big endian! ld l,(hl) ;03ca and 07fh ;03cb ld h,a ;03cd pop af ;03ce Removed saved DE from stack bit 6,h ;03cf Test bit 6 of H res 6,h ;03d1 Reset bit 6 of H push hl ;03d3 Push HL to stack call nz,l0a6ah ;03d4 Call EVAL_PAREN_INT if bit 6 of H set. ; (get function's argument) jp 02ba9h ;03d7 Jump to BASIC_LINK (Returns to address in HL) ; 'START (cont.)' ; =============== ; This is the rest of the boot code (it is executed only once, at power on) ;; START_2 l03dah: im 1 ;03da Set interrupt mode 1 ld iy,l00fdh ;03dc Load 00fdh into IY ; (default video interrupt hook) ld hl,027ffh ;03e0 Load latch address (27ffh) into HL ld (hl),l ;03e3 Load ffh into the latch (disable A7 clamp, ; empty character scanline) ld b,l ;03e4 Load ffh into B. ; This routine clears the entire RAM, starting at 2800h, if A holds 00h. l03e5h: inc hl ;03e5 Increment HL ld (hl),b ;03e6 Load B (ffh) into (HL) inc (hl) ;03e7 Increment (HL) ; (HL) should now hold 00h if the address in HL ; is mapped to RAM. jr nz,l03edh ;03e8 If not zero, break from loop ; (reached the end of RAM) or (hl) ;03ea If (HL) | A ... jr z,l03e5h ;03eb ... is zero, loop l03edh: ld (02a6ah),hl ;03ed Store the last address in RAM + 1 into RAM_TOP ld sp,02badh ;03f0 Set SP to 2badh ld hl,0c90bh ;03f3 Load c90bh into HL push hl ;03f6 Store c9h (ret) into 2bach (VIDEO_LINK) ; Store 0bh into 2babh dec sp ;03f7 Decrement SP (2baah) push hl ;03f8 Store c9h (ret) into 2ba9h (BASIC_LINK) ; Store 0bh into 2ba8h (HORIZ_POS) ld a,00ch ;03f9 Load 0ch (ASCII FF) into A rst 20h ;03fb Call PUTCH_RST ; This clears the screen and places cursor in ; the top left corner ; Execution now continues to the NEW command. DE is (probably) set to 0000h ; after reset (points to the start of the ROM). Since there is no valid ASCII ; string there the code below behaves as if NEW command was called without an ; argument. ; 'NEW' ; ===== ; "NEW" BASIC command line command. ;; NEW l03fch: call l0cd3h ;03fc Read command argument (BASIC offset) ; into HL (if any) ; (call STRING_TO_INT) ld de,02c3ah ;03ff Load 2c3ah (USER_MEM) into DE add hl,de ;0402 Add DE to HL ld sp,02c3ah ;0403 Load 2c3ah (USER_MEM) into SP push hl ;0406 Store HL to 2c38 (BASIC_END) push hl ;0407 Store HL to 2c36 (BASIC_START) l0408h: jp l0067h ;0408 Jump to RESET_BASIC ; 'RUN' ; ===== ; "RUN" BASIC command line command ;; RUN l040bh: call l0cd3h ;040b Read command argument ; (call STRING_TO_INT) ld de,(02c36h) ;040e Load BASIC_START address into DE l0412h: jr l0422h ;0412 Jump to RUN_DO ; 'CONTINUE' ; ========== ; Continue execution of a BASIC program. ; A call to this function will try to execute the next command on the line ; or will continue to the next line. If a valid command is found this function ; never returns. On the other hand if neither ':' nor line end is encountered, ; it returns. ;; CONTINUE l0414h: ld (02bb5h),a ;0414 Load A into FIXME rst 18h ;0417 Call READ_PAR db ':' ;0418 db l041dh-$-1 ;0419 Check for ':'. If not found jump to CONTINUE_CR ;; ELSE l041ah: pop af ;041a Remove return address from stack. jr l042dh ;041b Execute the rest of the line ; (jump to RUN_CONT_LINE) ;; CONTINUE_CR l041dh: rst 18h ;041d Call READ_PAR db 00dh ;041e db l0440h-$-1 ;041f Check for ASCII CR. If not found return. pop af ;0420 Remove return address from stack and ; execute next BASIC line. ; 'INTERPRET NEXT LINE OF BASIC' ; ============================= ; Jump to this location finds the next BASIC line following DE and starts BASIC ; interpreter at the BASIC. ;; RUN_NEXT_LINE l0421h: rst 28h ;0421 Call CLEAR_HL ;; RUN_DO l0422h: call l07f6h ;0422 Call FIND_LINE_SCOPE jr c,l0408h ;0425 Jump to RESET_BASIC if line was not found. ; 'INTERPRET A LINE OF BASIC' ; ============================= ; Jump to this location starts BASIC interpreter at the BASIC line at DE. ;; RUN_THIS_LINE l0427h: ld (02a9fh),de ;0427 Load DE into BASIC_LINE inc de ;042b DE = DE + 2 inc de ;042c (points to the ASCII part of the line) ; 'INTERPRET THE REST OF THE LINE' ; ================================ ; Jump to this location interprets the rest of the line at DE. ;; RUN_CONT_LINE l042dh: call l02ffh ;042d Call CHECK_BREAK ld ix,02aach ;0430 Load ARITHM_STACK into IX ld l,(l0f30h-1)&00ffh ;0434 Load 2fh into L jp l0398h ;0436 Jump to PARSE with pointer to the ; BASIC_CMD_TABLE. ; 'EVALUATE RELATIONAL OPERATORS (cont.)' ; ======================================= ; Fetches the next expression at DE and compares its value with the top of ; the stack. ; Returns: ; HL: 0000h ; Flags: result of comparisson ;; EVAL_RELATIONAL_2 l0439h: call l068eh ;0439 Call EVAL_ARITHMETIC call l0b10h ;043c Call FP_COMPARE rst 28h ;043f Call CLEAR_HL l0440h: ret ;0440 Return ;; IF l0441h: rst 8 ;0441 cf ld a,h ;0442 7c | or l ;0443 b5 jr nz,l042dh ;0444 20 e7 call l081ch ;0446 cd 1c 08 l0449h: jr nc,l0427h ;0449 Jump to RUN_THIS_LINE jr l0408h ;044b ...else jump to RESET_BASIC ; 'COMMENT' ; ========= ; "!" BASIC command. Does nothing. NOTE: An ELSE with IF also points here. ;; COMMENT l044dh: rst 28h ;044d Call CLEAR_HL call l0813h ;044e Find next line (call FIND_LINE_NEXT_2) jr l0449h ;0451 ; 'GOTO' ; ====== ; "GOTO" BASIC command. ;; GOTO l0453h: rst 8 ;0453 Get command argument push de ;0454 Save DE on stack call l07f2h ;0455 Call FIND_LINE. jp nz,l065bh ;0458 Jump to HOW_RST, if exact line number in ; argument was not found. l045bh: pop af ;045b Remove saved DE from stack. jr l0427h ;045c Jump to RUN_THIS_LINE. ; 'LIST' ; ====== ; "LIST" BASIC command line command. ;; LIST l045eh: call l0cd3h ;045e Read argument into HL (call STRING_TO_INT) ; Note: if called from KEY, HL contains 0000h. ;; LIST_KEY l0461h: call l02edh ;0461 Move cursor to a new line (call NEWLINE) call l07f2h ;0464 Find the required BASIC line (call FIND_LINE) l0467h: jr c,l0408h ;0467 Jump to RESET_BASIC if line not found ; Keep printing BASIC lines as long as RETURN or LIST key is pressed. ;; LIST_LOOP l0469h: call l0931h ;0469 Call PRINT_BASIC_LINE call l07f6h ;046c Find the following line (call FIND_LINE_SCOPE) jr c,l0467h ;046f Jump to RESET_BASIC if line not found ;; LIST_KEY_LOOP l0471h: call l02ffh ;0471 Call CHECK_BREAK ld a,(02030h) ;0474 Load "RETURN" key status into A ld hl,02034h ;0477 Load "LIST" key address into HL and (hl) ;047a rrca ;047b jr nc,l0469h ;047c If either "RETURN" or "LIST" is pressed, loop ; to LIST_LOOP jr l0471h ;047e Else loop to LIST_KEY_LOOP ; 'PRINT' ; ======= ; "PRINT" BASIC command. ;; PRINT l0480h: rst 18h ;0480 Call READ_PAR db ':' ;0481 db l0488h-$-1 ;0482 ; If ':' follows the PRINT command, just print ; a carriage return and continue execution ; at the rest of the line. ld a,00dh ;0483 Print ASCII CR rst 20h ;0485 (call PUTCH_RST) jr l042dh ;0486 Jump to RUN_CONT_LINE l0488h: rst 18h ;0488 Call READ_PAR db 00dh ;0489 (ASCII CR) db l04d1h-$-1 ;048a ; Jump to PRINT_DO if there is an argument ; Just PRINT without and argument prints a ; carriage return. rst 20h ;048b Print ASCII CR l048ch: jr l0421h ;048c Jump to RUN_NEXT_LINE ;; PRINTSTR_QUOTE l048eh: rst 18h ;048e Call READ_PAR db '"' ;048f db l04e5h-$-1 ;0490 ; If the argument doesn't begin with a quote ; evaluate its contents. Jump to FIXME ; If the argument begins with a quote, print ; the quoted string. call l0938h ;0491 Print string following command ending with ; '"'. (call PRINTSTR_A) jr nz,l048ch ;0494 No closing '"' found. Ignore this error and ; jump to RUN_NEXT_LINE jr l04adh ;0496 Jump FIXME ;; X$ l0498h: ld l,05ch ;0498 2e 5c . \ db 001h ;049a Dummy ld bc,nn (skip next instruction) ;; Y$ l049bh: ld l,060h ;049b l049dh: ld h,02ah ;049d 26 2a & * call l060eh ;049f cd 0e 06 l04a2h: ld a,(hl) ;04a2 7e ~ inc hl ;04a3 23 # or a ;04a4 b7 jr z,l04adh ;04a5 28 06 ( rst 20h ;04a7 e7 ld a,l ;04a8 7d } and 00fh ;04a9 e6 0f jr nz,l04a2h ;04ab 20 f5 l04adh: rst 18h ;04ad Call READ_PAR db ',' ;04ae db l04cbh-$-1 ;04af l04b0h: ld a,(02a68h) ;04b0 3a 68 2a : h * and 007h ;04b3 e6 07 jr z,l04ceh ;04b5 28 17 ( ld a,020h ;04b7 3e 20 > rst 20h ;04b9 e7 jr l04b0h ;04ba 18 f4 ;; AT l04bch: rst 8 ;04bc cf ld a,h ;04bd 7c | or 028h ;04be f6 28 ( and 029h ;04c0 e6 29 ) ld h,a ;04c2 67 g ld (02a68h),hl ;04c3 22 68 2a " h * rst 18h ;04c6 df inc l ;04c7 2c , ld (bc),a ;04c8 02 jr l04ceh ;04c9 18 03 l04cbh: rst 18h ;04cb Call READ_PAR db ';' ;04cc db l04e1h-$-1 ;04cd l04ceh: call l0414h ;04ce cd 14 04 ; 'PRINT (cont.)' ; =============== ;; PRINT_DO l04d1h: ld l,(l0fe1h-1)&00ffh ;04d1 Jump to PARSE with pointer to the jp l0398h ;04d3 BASIC_PRINT_TABLE ; 'HOME' ; ====== ; "HOME" BASIC command ;; HOME l04d6h: call l0cd3h ;04d6 Get command argument (call STRING_TO_INT) ld (02a6ch),hl ;04d9 Store argument into WINDOW_LEN jr nz,l04e4h ;04dc If there was a non-zero argument, that is all. ; Call BASIC_NEXT ld a,00ch ;04de Load ASCII FF into A db 001h ;04e0 Dummy "ld bc,nn" (skip next two instructions) l04e1h: ld a,00dh ;04e1 Load ASCII CR into A rst 20h ;04e3 Call PUTCH_RST l04e4h: rst 30h ;04e4 Call BASIC_NEXT l04e5h: call l0396h ;04e5 cd 96 03 jr nz,l04f1h ;04e8 20 07 call l0ab2h ;04ea cd b2 0a call l08f6h ;04ed cd f6 08 ; following two lines from Diss_019.jpg db 03eh ;04f0 Dummy LD A, l04f1h: rst 20h ;04f1 e7 > jr l04adh ;04f2 18 b9 ;; CALL l04f4h: call l0974h ;04f4 cd 74 09 t rst 8 ;04f7 cf push de ;04f8 d5 call l07f2h ;04f9 cd f2 07 jp nz,l065bh ;04fc c2 5b 06 [ ld hl,(02a9fh) ;04ff 2a 9f 2a * * push hl ;0502 e5 ld hl,(02aa3h) ;0503 2a a3 2a * * push hl ;0506 e5 rst 28h ;0507 ef ld (02aa1h),hl ;0508 22 a1 2a " * add hl,sp ;050b 39 9 ld (02aa3h),hl ;050c 22 a3 2a " * jp l0427h ;050f c3 27 04 ' ;; RET l0512h: ld hl,(02aa3h) ;0512 2a a3 2a * * ld a,h ;0515 7c | or l ;0516 b5 jp z,l065ah ;0517 ca 5a 06 Z ld sp,hl ;051a f9 pop hl ;051b e1 ld (02aa3h),hl ;051c 22 a3 2a " * pop hl ;051f e1 ld (02a9fh),hl ;0520 22 9f 2a " * pop de ;0523 d1 l0524h: call l0959h ;0524 cd 59 09 Y rst 30h ;0527 f7 ;; STEP l0528h: rst 8 ;0528 cf db 001h ;0529 Dummy ld bc,nn (skip next two instructions) l052ah: rst 28h ;052a inc hl ;052b ld (02a91h),hl ;052c 22 91 2a " * ld hl,(02a9fh) ;052f 2a 9f 2a * * ld (02a93h),hl ;0532 22 93 2a " * ex de,hl ;0535 eb ld (02a95h),hl ;0536 22 95 2a " * ld bc,l0005h+5 ;0539 01 0a 00 ld hl,(02aa1h) ;053c 2a a1 2a * * ex de,hl ;053f eb rst 28h ;0540 ef add hl,sp ;0541 39 9 ; Following two lines from Diss_019.jph db 03eh ;0542 Dummy LD A, l0543h: add hl,bc ;0543 09 > ld a,(hl) ;0544 7e ~ inc hl ;0545 23 # or (hl) ;0546 b6 jr z,l055fh ;0547 28 16 ( ld a,(hl) ;0549 7e ~ dec hl ;054a 2b + cp d ;054b ba jr nz,l0543h ;054c 20 f5 ld a,(hl) ;054e 7e ~ cp e ;054f bb jr nz,l0543h ;0550 20 f1 ex de,hl ;0552 eb rst 28h ;0553 ef add hl,sp ;0554 39 9 ld b,h ;0555 44 D ld c,l ;0556 4d M ld hl,l0005h+5 ;0557 21 0a 00 ! add hl,de ;055a 19 call l094ch ;055b cd 4c 09 L ld sp,hl ;055e f9 l055fh: ld hl,(02a95h) ;055f 2a 95 2a * * ex de,hl ;0562 eb rst 30h ;0563 f7 ; 'NEXT' ; ====== ; "NEXT" BASIC command ;; NEXT l0564h: call l078bh ;0564 Call LOCATE_VARIABLE_ERR ld (02a9bh),hl ;0567 Load variable location into NEXT_VAR l056ah: push de ;056a Save parser address on stack ex de,hl ;056b DE = NEXT_VAR ld hl,(02aa1h) ;056c Load FOR_POINTER into HL ld a,h ;056f or l ;0570 jp z,l065bh ;0571 Jump to HOW_RST if FOR_POINTER is 0000h rst 10h ;0574 Run CMP_HL_DE jr z,l0580h ;0575 Jump to FIXME if FOR_POINTER == NEXT_VAR pop de ;0577 Remove DE from stack call l0959h ;0578 cd 59 09 Y ld hl,(02a9bh) ;057b 2a 9b 2a * * jr l056ah ;057e 18 ea l0580h: call l0a45h ;0580 Call PUSH_FP call l0a6dh ;0583 Call FP_TO_INT ex de,hl ;0586 HL = NEXT variable address ; DE = NEXT variabe value ld hl,(02a91h) ;0587 Load FOR_STEP value into HL push hl ;058a Save FOR_STEP to the stack add hl,de ;058b HL = current NEXT variable value + FOR_STEP push hl ;058c Save HL on stack call l0abch ;058d Call INT_TO_FP ld hl,(02aa1h) ;0590 Load FOR_POINTER into HL call l073bh ;0593 Store new value into variable (call POP_FP) pop de ;0596 DE = value of the NEXT variable ld hl,(02a6eh) ;0597 HL = FOR_TO value pop af ;059a AF = FOR_STEP rlca ;059b 07 jr nc,l059fh ;059c 30 01 0 ex de,hl ;059e eb l059fh: ld a,h ;059f 7c | xor d ;05a0 aa jp p,l05a5h ;05a1 f2 a5 05 ex de,hl ;05a4 eb l05a5h: rst 10h ;05a5 d7 pop de ;05a6 d1 jp c,l0524h ;05a7 da 24 05 $ ld hl,(02a93h) ;05aa 2a 93 2a * * ld (02a9fh),hl ;05ad 22 9f 2a " * jr l055fh ;05b0 18 ad l05b2h: jp z,l0736h ;05b2 ca 36 07 6 l05b5h: push hl ;05b5 e5 call l05fch ;05b6 cd fc 05 jr c,l05cah ;05b9 38 0f 8 jr z,l05e2h ;05bb 28 25 ( % ex (sp),hl ;05bd e3 pop bc ;05be c1 l05bfh: ld a,(bc) ;05bf 0a or a ;05c0 b7 jr z,l05edh ;05c1 28 2a ( * inc bc ;05c3 03 call l017eh ;05c4 cd 7e 01 ~ jr nz,l05bfh ;05c7 20 f6 ret ;05c9 c9 l05cah: pop hl ;05ca e1 rst 18h ;05cb df ; Following lines from Diss_020.jpg db '"' ;05cc db 000h ;05cd l05ceh: call l05d9h ;05ce cd d9 05 jr z,l05edh ;05d1 28 1a ( inc de ;05d3 13 call l017eh ;05d4 cd 7e 01 ~ jr nz,l05ceh ;05d7 20 f5 l05d9h: ld a,(de) ;05d9 1a cp 00dh ;05da fe 0d ret z ;05dc c8 cp 022h ;05dd fe 22 " ret nz ;05df c0 inc de ;05e0 13 ret ;05e1 c9 l05e2h: dec de ;05e2 1b call l0396h ;05e3 cd 96 03 jr z,l05cah ;05e6 28 e2 ( pop hl ;05e8 e1 call l017eh ;05e9 cd 7e 01 ~ ret z ;05ec c8 l05edh: rst 18h ;05ed df dec hl ;05ee 2b + ld (bc),a ;05ef 02 jr l05b5h ;05f0 18 c3 ld (hl),000h ;05f2 36 00 6 l05f4h: call l017fh ;05f4 cd 7f 01 ret z ;05f7 c8 ld (hl),030h ;05f8 36 30 6 0 jr l05f4h ;05fa 18 f8 l05fch: call l0125h ;05fc cd 25 01 % ret c ;05ff d8 l0600h: dec de ;0600 1b ld a,(de) ;0601 1a inc de ;0602 13 cp 029h ;0603 fe 29 ) ret z ;0605 c8 ld a,(de) ;0606 1a cp 024h ;0607 fe 24 $ jr z,l060dh ;0609 28 02 ( ;; FIXME catch all l060bh: xor a ;060b af ret ;060c c9 l060dh: inc de ;060d 13 l060eh: ld a,l ;060e 7d } sub 05ch ;060f d6 5c \ jr nz,l061dh ;0611 20 0a ld l,070h ;0613 2e 70 . p rst 18h ;0615 df jr z,l061bh ;0616 28 03 ( call l0114h ;0618 cd 14 01 l061bh: or h ;061b b4 ret ;061c c9 l061dh: cp 007h ;061d fe 07 jp nc,l078fh ;061f d2 8f 07 ld l,080h ;0622 2e 80 . or h ;0624 b4 ret ;0625 c9 ;; TAKE l0626h: call l05fch ;0626 cd fc 05 jr c,l0663h ;0629 38 38 8 8 push de ;062b d5 push af ;062c f5 push hl ;062d e5 ld de,(02a9dh) ;062e ed 5b 9d 2a [ * ld hl,(02c38h) ;0632 2a 38 2c * 8 , l0635h: rst 18h ;0635 df inc hl ;0636 23 # ld (bc),a ;0637 02 jr l063dh ;0638 18 03 rst 18h ;063a df inc l ;063b 2c , rrca ;063c 0f l063dh: pop hl ;063d e1 pop af ;063e f1 call l05b2h ;063f cd b2 05 l0642h: ld (02a9dh),de ;0642 ed 53 9d 2a S * pop de ;0646 d1 rst 18h ;0647 df inc l ;0648 2c , ld b,e ;0649 43 C jr l0626h ;064a 18 da l064ch: ld a,(de) ;064c 1a inc de ;064d 13 cp 00dh ;064e fe 0d jr nz,l064ch ;0650 20 fa inc de ;0652 13 inc de ;0653 13 rst 10h ;0654 d7 jr nc,l0635h ;0655 30 de 0 pop hl ;0657 e1 l0658h: pop af ;0658 f1 ; from Diss_020.jph db 00eh ;0659 Dummy LD,C ; '"HOW?" ERROR RESTART' ; ======================= ; This restart is called when an argument for a function or command is invalid. ; (for example out of range) ;; HOW_RST_PUSH_DE l065ah: push de ;065a Push current parser address on stack. ;; HOW_RST l065bh: call l0799h ;065b Call BASIC_ERROR dm "HOW?", 00dh ;065e l0663h: rst 8 ;0663 cf push de ;0664 d5 call l07f2h ;0665 cd f2 07 inc de ;0668 13 inc de ;0669 13 jr l0642h ;066a 18 d6 ;; INPUT l066ch: push de ;066c d5 ld a,03fh ;066d 3e 3f > ? call l07bdh ;066f cd bd 07 ld de,02bb6h ;0672 11 b6 2b + rst 18h ;0675 df dec c ;0676 0d dec b ;0677 05 pop de ;0678 d1 call l05fch ;0679 cd fc 05 rst 30h ;067c f7 pop de ;067d d1 push de ;067e d5 call l05fch ;067f cd fc 05 jr c,l065bh ;0682 38 d7 8 push de ;0684 d5 ld de,02bb6h ;0685 11 b6 2b + call l05b2h ;0688 cd b2 05 pop de ;068b d1 pop af ;068c f1 rst 30h ;068d f7 ; 'EVALUATE ARITHMETIC OPERATORS' ; =============================== ; Evaluates an arithmetic expression at DE and returns its value on arithmetic ; stack. ;; EVAL_ARITHMETIC l068eh: ; Start by checking for a leading "+" or "-" character. rst 18h ;068e Call READ_PAR db '-' ;068f db l0697h-$-1 ;0690 ; Leading "-" sign - add an implicit 0 to arithmetic stack and jump to ; subtraction. rst 28h ;0691 Call CLEAR_HL call l0abch ;0692 Call INT_TO_FP jr l06abh ;0695 Jump to subtraction l0697h: rst 18h ;0697 Call READ_PAR db '+' ;0698 db 000h ;0699 ; Ignore a leading plus sign. Load level 2 value to arithmetic stack. call l06b3h ;069a Call EVAL_LVL2 ; Level 1 - Evaluation of addition and subtraction ;; EVAL_LVL1 l069dh: rst 18h ;069d Call READ_PAR db '+' ;069e db l06a8h-$-1 ;069f ; Addition. Load a value from level 2 and add them together call l06b3h ;06a0 Call EVAL_LVL2 call l0b32h ;06a3 Call FP_ADD jr l069dh ;06a6 Jump back to EVAL_LVL1 l06a8h: rst 18h ;06a8 Call READ_PAR db '-' ;06a9 db l078ah-$-1 ;06aa Return if operand is neither "+" nor "-" ; Subtraction. Load a value from level 2 and add them together l06abh: call l06b3h ;06ab Call EVAL_LVL2 call l0b1eh ;06ae Call FP_SUB jr l069dh ;06b1 Jump back to EVAL_LVL1 ; Level 2 - Evaluation of multiplication and division ;; EVAL_LVL2 l06b3h: call l0393h ;06b3 Call EVAL_FUNCTIONS l06b6h: rst 18h ;06b6 Call READ_PAR db '*' ;06b7 db l06c1h-$-1 ;06b8 ; Multiplication call l0393h ;06b9 Call EVAL_FUNCTIONS call l0ae6h ;06bc Call FP_MUL jr l06b6h ;06bf Return back to level 2 evaluation l06c1h: rst 18h ;06c1 Call READ_PAR db '/' ;06c2 db l078ah-$-1 ;06c3 Return if operand is neither "+" nor "-" ; Division call l0393h ;06c4 Call EVAL_FUNCTIONS call l0af7h ;06c7 Call FP_DIV jr l06b6h ;06ca Return back to level 2 evaluation ; 'UNDOT' ; ======= ; "UNDOT" BASIC command. ;; UNDOT l06cch: ld a,001h ;06cc Load 01h into A (Zf = 0, Sf = 0) db 001h ;06ce Dummy ld bc,nn (skip next instruction) ; 'DOT' ; ===== ; "DOT" BASIC command. ;; DOT l06cfh: ld a,080h ;06cf Load 80h into A (Zf = 0, Sf = 1) push af ;06d1 Store AF on stack rst 18h ;06d2 Call READ_PAR db '*' ;06d3 db l06dah-$-1 ;06d4 ; DOT* and UNDOT* versions of command: turn clock on or off pop af ;06d5 Restore AF from stack ld (02bafh),a ;06d6 Store A into CLOCK_ON rst 30h ;06d9 Continue with the next command (call BASIC_NEXT) l06dah: pop af ;06da Restore AF from stack db 006h ;06db Dummy "ld b,nn" (skip next two commands so that ; flags remain unchanged) ; 'DOT FUNCTION' ; ============== ; "DOT" BASIC function. ;; DOT_FUNC l06dch: xor a ;06dc Clear A, set Zf and clear Sf and a ;06dd ; Meaning of flags at this point: ; ; Zf Sf function ; ----------------------------------------- ; 1 x Query pixel (DOT function) ; 0 0 Clear pixel (UNDOT command) ; 0 1 Draw pixel (DOT command) push af ;06de Save A on stack rst 8 ;06df Get X coordinate (call EVAL_INT_EXP) push hl ;06e0 Save X on stack call l0005h ;06e1 Get Y coordinate (call EVAL_INT_EXP_NEXT) push de ;06e4 Save DE on stack ex de,hl ;06e5 Move Y to DE (actually E, since Y < 48) ld bc,32 ;06e6 Load 32 into BC inc e ;06e9 Increment E ld hl,02800h ;06ea Load VIDEO_MEM into HL ; HL = 2800h + 32 * (E / 3) ; A = 1 << (E % 3) ;; DOT_DIV_3_LOOP l06edh: ld d,3 ;06ed Load 3 into D ld a,1 ;06ef Load 1 into A ;; DOT_DIV_1_LOOP l06f1h: dec e ;06f1 Decrement E jr z,l06feh ;06f2 Break from loop if E = 0 rlca ;06f4 Rotate A left 2 times rlca ;06f5 dec d ;06f6 Decrement D, loop to DOT_DIV_1_LOOP if not zero jr nz,l06f1h ;06f7 add hl,bc ;06f9 Add 32 to HL for each 3 decrements of E res 1,h ;06fa HL = HL & 01ff jr l06edh ;06fc Loop to DOT_DIV_3_LOOP ; HL contains address of the character line that holds the required graphics ; coordinates. l06feh: ld b,a ;06fe Save A to B (?) pop de ;06ff Restore DE from stack ex (sp),hl ;0700 Save line address to stack, move X to HL res 7,l ;0701 X = X & 001f res 6,l ;0703 srl l ;0705 X = X / 2 jr nc,l070ah ;0707 Shift A left if X odd rlca ;0709 l070ah: ld h,000h ;070a Clear H pop bc ;070c Get line address from stack add hl,bc ;070d Add X / 2. ld b,a ;070e Save A to B pop af ;070f Restore flags from stack ld a,b ;0710 ; HL now contains the address of the character, A contains the bitmask. jr nz,l071fh ;0711 If Zf not set, jump to DOT_DRAW_OR_CLEAR ; Check if pixel is set and return a floating point value 1 or 0 bit 7,(hl) ;0713 If (HL) is not graphics character (bit 7 not jr z,l0718h ;0715 set), jump over and return 0. and (hl) ;0717 Check if bit in A is set in (HL) l0718h: rst 28h ;0718 Call CLEAR_HL jr z,l071ch ;0719 If bit was set HL = 1 else HL = 0 inc hl ;071b l071ch: jp l0abch ;071c Jump to INT_TO_FP and return ; Draw or clear the pixel ;; DOT_DRAW_OR_CLEAR l071fh: push af ;071f Save flags on stack bit 7,(hl) ;0720 If (HL) is not already a graphics character jr nz,l0726h ;0722 write a blank character (80h). ld (hl),080h ;0724 l0726h: pop af ;0726 Restore flags from stack jp m,l072dh ;0727 If sign negative, draw a pixel, else clear ; a pixel. cpl ;072a Clear bit in A in (HL) and (hl) ;072b db 006h ;072c Dummy ld b,nn (skip next instruction) l072dh: or (hl) ;072d Set bit in A in (HL) ld (hl),a ;072e Store result back to (HL) rst 30h ;072f Continue with the next command (call BASIC_NEXT) l0730h: call l078bh ;0730 Call LOCATE_VARIABLE_ERR rst 18h ;0733 Call READ_PAR db '=' ;0734 db l078fh-$-1 ;0735 Else jump to WHAT_RST l0736h: push hl ;0736 e5 call l0ab2h ;0737 cd b2 0a pop hl ;073a e1 ; 'POP FLOATING POINT NUMBER FROM STACK' ; ====================================== ; Fetches a floating point number from the top of the arithmetic stack and ; stores it in 4-byte format at (HL). ; IX Offset 0 1 2 3 4 ; --------------------------------------------------- ; 24 bit mantissa (MSB) Exp Sign ; IX Offset 0 1 2 3 4 ; ------------------------------------------------------------- ; MMMMMMMM MMMMMMMM 1MMMMMMM EEEEEEEE S0000000 ; (LSB) (MSB) ; HL Offset 0 1 2 3 ; -------------------------------------------------- ; MMMMMMMM MMMMMMMM EMMMMMMM SEEEEEEE ; (LSB) (MSB) ;; POP_FP l073bh: call l090eh ;073b Call SUB_IX_5 ld bc,0004h ;073e Load 4 into BC push de ;0741 Save HL and DE to stack push hl ;0742 ex de,hl ;0743 Store HL to DE push ix ;0744 Store IX to HL pop hl ;0746 ldir ;0747 Transfer 4 bytes from (IX) to (HL) ; DE = DE_orig + 4 ex de,hl ;0749 Restore HL and DE dec hl ;074a HL = HL_orig + 2 dec hl ;074b rl (hl) ;074c Rotate left (HL_orig + 2) inc hl ;074e HL = HL_orig + 3 ld a,(ix+4) ;074f Sign bit to Cf rla ;0752 rr (hl) ;0753 Store sign bit to MSB of (HL_orig + 3) ; LSB of exponent to Cf. dec hl ;0755 HL = HL_orig + 2 rr (hl) ;0756 Store LSB of exponent in MSB of (HL_orig + 2) pop hl ;0758 Restore HL and DE from stack pop de ;0759 ret ;075a Return ;; CATCH-ALL-COMMAND l075bh: call l05fch ;075b cd fc 05 jr c,l0768h ;075e 38 08 8 push af ;0760 f5 rst 18h ;0761 df dec a ;0762 3d = dec hl ;0763 2b + pop af ;0764 f1 call l05b2h ;0765 cd b2 05 l0768h: rst 30h ;0768 f7 ;; PTR l0769h: ld h,d ;0769 62 b ld l,e ;076a 6b k call l05fch ;076b cd fc 05 jr l071ch ;076e 18 ac ;; VAL l0770h: push de ;0770 d5 ex de,hl ;0771 eb call l0ab2h ;0772 cd b2 0a pop de ;0775 d1 ret ;0776 c9 ; 'BASIC NUMERIC EXPRESSION EVALUATION' ; ===================================== ; This function is called if no other BASIC function matches. It evaluates a ; numeric expression at DE. ;; NUMBER l0777h: call l0125h ;0777 Call LOCATE_VARIABLE jp nc,l0a45h ;077a Store variable contents on stack and return ; (call PUSH_FP) ; Not a variable call l01a2h ;077d Call STRING_TO_FP ret nz ;0780 Return if valid. ; Not a constant ; 'EVALUATE PARENTHESIS' ; ====================== ; This function evaluates an expression enclosed in parenthesis at DE ; and returns its value on the top of arithmetic stack. ;; EVAL_PAREN l0781h: rst 18h ;0781 Call READ_PAR db '(' ;0782 db l078fh-$-1 ;0783 If '(' not found, jump to WHAT_RST call l0ab2h ;0784 Call EVAL_EXPRESSION rst 18h ;0787 Call READ_PAR db ')' ;0788 db l078bh-$-1 ;0789 l078ah: ret ;078a Return if closing ')' found ; Closing ')' not found. ; 'LOCATE VARIABLE OR ERROR' ; ========================== ; Like LOCATE_VARIABLE, but jumps to "WHAT?" error if variable is not found. ;; LOCATE_VARIABLE_ERR l078bh: call l0125h ;078b Call LOCATE_VARIABLE ret nc ;078e Return if variable found, else continue to ; WHAT_RST ; '"WHAT?" ERROR RESTART' ; ======================= ; This restart is called when an unknown function or command is called. ;; WHAT_RST l078fh: push de ;078f Store current parser address on stack. call l0799h ;0790 Store address of the "WHAT?" string ; on the stack and call BASIC_ERROR. dm "WHAT?", 00dh ;0793 ; 'BASIC ERROR' ; ============= ; Pointer to the error message is the first 16 bit value on the CPU stack. ; Current parser address is the second 16 bit value on the CPU stack. ;; BASIC_ERROR l0799h: pop de ;0799 Load address of the error message into DE call l0937h ;079a Call PRINTSTR ld de,(02a9fh) ;079d Load BASIC_LINE into DE ld a,e ;07a1 or d ;07a2 Set Z flag if BASIC_LINE is zero. ld hl,l0317h ;07a3 Load RESET_BASIC_2 address into ex (sp),hl ;07a6 the address at the top of the stack ; and store stack value into HL. ; Top of the stack stores the current parser ; address or return address of the calling ; function. ret z ;07a7 Return into RESET_BASIC_2 if BASIC_LINE = 0 rst 10h ;07a8 Call CMP_HL_DE ret c ;07a9 Return into RESET_BASIC_2 if ; BASIC_LINE > current parser address ; (stack did not contain a valid parser address) ld c,(hl) ;07aa Save character at current parser addr. into C push bc ;07ab Store BC on stack ld (hl),000h ;07ac Store 00h (ASCII NUL) to current parser addr. push hl ;07ae Store HL on stack call l0931h ;07af Call PRINT_BASIC_LINE pop hl ;07b2 Restore BC and HL from stack. pop bc ;07b3 ld a,03fh ;07b4 Load ASCII '?' into A l07b6h: dec de ;07b6 DE = BASIC_LINE - 1 ld (hl),c ;07b7 Restore character at the current parser ; address. jp l0936h ;07b8 Jump to PUTCH_PRINTSTR and return into ; RESET_BASIC_2 ; 'GET STRING FROM KEYBOARD' ; ========================== ; This function reads a string from the keyboard into the input buffer. String ; is terminated with ASCII CR. Some minimal editing is supported ("LEFT" key ; acts as a backspace, "SHIFT-DELECT" clears the buffer and screen) ; Returns: ; DE: Address of the last character in the string + 1 ;; GETSTR l07bbh: ld a,'>' ;07bb Load prompt into A l07bdh: ld de,02bb6h ;07bd Load INPUT_BUFFER address into DE rst 20h ;07c0 Display prompt. ;; GETSTR_LOOP_CURS l07c1h: exx ;07c1 ld (hl),'_' ;07c2 Print cursor at location (HL') exx ;07c4 Note: HL' is set by PUTCH_RST. ;; GETSTR_LOOP l07c5h: call l0cf5h ;07c5 Read a key and print it (call KEY) rst 20h ;07c8 (call PUTCH_RST) exx ;07c9 ld (hl),'_' ;07ca Print cursor exx ;07cc cp 00dh ;07cd jr z,l07ddh ;07cf "ENTER" pressed, jump to GETSTR_ADD cp 01dh ;07d1 jr z,l07eah ;07d3 "LEFT" pressed, jump to GETSTR_BACKSPACE cp 00ch ;07d5 jr z,l07bbh ;07d7 "SHIFT-DELETE" pressed - begin from the start cp 020h ;07d9 jr c,l07c5h ;07db Ignore if key code is less than 20h otherwise ;; GETSTR_ADD l07ddh: ld (de),a ;07dd Load ASCII code of the pressed key into the inc de ;07de buffer and increment DE cp 00dh ;07df If "ENTER" was pressed, return. ret z ;07e1 ld a,e ;07e2 Compare DE with 2c36h (end of buffer) cp 034h ;07e3 jr nz,l07c5h ;07e5 If end of buffer not reached, ; loop to GETSTR_LOOP... ld a,01dh ;07e7 ...else delete last pressed character from the rst 20h ;07e9 screen and buffer. ;; GETSTR_BACKSPACE l07eah: ld a,e ;07ea Compare DE with 2bb6h (start of buffer) cp 0b6h ;07eb jr z,l07bbh ;07ed If not at start of buffer decrement DE dec de ;07ef jr l07c1h ;07f0 Jump to GETSTR_LOOP_CURS ; 'FIND BASIC LINE' ; ================= ; Finds BASIC line with line number equal or greater than HL in the entire ; BASIC code. ;; FIND_LINE l07f2h: ld de,(02c36h) ;07f2 Load BASIC_START into DE and continue with ; FIND_LINE_SCOPE ; 'FIND BASIC LINE IN SCOPE' ; ========================== ; Finds BASIC line with line number equal or greater than HL, starting at DE. ; If line number was not found after DE, Cf is set. ; Parameters: ; DE: Pointer to the start of a BASIC line where the search starts. ; HL: Line number to find. ; Returns: ; DE: Pointer to the start of the BASIC line if a matching line number ; is found ; Cf: Set if line number was not found. ; Zf: Set if line number found is equal to HL. ;; FIND_LINE_SCOPE l07f6h: push hl ;07f6 Save HL on stack ; Check if DE is within the expected margins of the BASIC program. ld hl,(02c36h) ;07f7 Load BASIC_START-1 into HL dec hl ;07fa rst 10h ;07fb (call CMP_HL_DE) jp nc,l0317h ;07fc Jump to RESET_BASIC_2 if DE < BASIC_START ld hl,(02c38h) ;07ff Load BASIC_END-1 into HL dec hl ;0802 rst 10h ;0803 (call CMP_HL_DE) pop hl ;0804 Restore HL ret c ;0805 Return if DE >= BASIC_END ld a,(de) ;0806 Subtract HL from 16-bit line number at (DE) sub l ;0807 ld b,a ;0808 inc de ;0809 ld a,(de) ;080a sbc a,h ;080b BA = (DE)-HL l080ch: jr c,l0812h ;080c Jump to FIND_LINE_NEXT if HL > line number dec de ;080e Decrement DE or b ;080f Check if HL is equal to line number at (DE) ret ;0810 Return l0811h: inc de ;0811 Increment DE ; Increments DE until start of the next BASIC line. ;; FIND_LINE_NEXT l0812h: inc de ;0812 Increment DE (points to first character after ; line number) ; 'FIND NEXT BASIC LINE' ; ======================== ; A call to this location moves DE to the start of the next BASIC line if ; HL contains 0000h. ;; FIND_LINE_NEXT_2 l0813h: ld a,(de) ;0813 Load character into A cp 00dh ;0814 jr nz,l0812h ;0816 Loop until equal to CR l0818h: inc de ;0818 Increment DE (points to line number of the next ; line) jr l07f6h ;0819 Loop ;; FIXME: catch all l081bh: inc de ;081b 13 l081ch: ld a,(de) ;081c 1a rst 28h ;081d ef cp 00dh ;081e fe 0d jr z,l0818h ;0820 28 f6 ( cp 021h ;0822 fe 21 ! jr z,l0812h ;0824 28 ec ( cp 022h ;0826 fe 22 " jr nz,l0834h ;0828 20 0a l082ah: inc de ;082a 13 ld a,(de) ;082b 1a cp 00dh ;082c fe 0d jr z,l0818h ;082e 28 e8 ( cp 022h ;0830 fe 22 " jr nz,l082ah ;0832 20 f6 l0834h: cp 045h ;0834 fe 45 E jr nz,l081bh ;0836 20 e3 ld l,0f6h ;0838 2e f6 . jp l0398h ;083a c3 98 03 ; 'PRINT A FLOATING POINT NUMBER (cont.)' ; ======================================= ; Prints a floating point number on the top of the arithmetic stack at the ; current cursor position. Number must not be zero. ; IX Offset -5 -4 -3 -2 -1 ; --------------------------------------------------- ; 24 bit mantissa (MSB) Exp Sign ;; PRINTFP_DO ; First print the sign character and make the floating point number positive. l083dh: ld a,(ix-1) ;083d Load sign bit into A and a ;0840 ld a,' ' ;0841 If sign is positive, store ASCII ' ' in A ; else store ASCII '-'. jr z,l0847h ;0843 ld a,'-' ;0845 l0847h: rst 20h ;0847 Print sign character (call PUTCH_RST) xor a ;0848 ld (ix-1),a ;0849 Clear sign bit. dec a ;084c A = -1 ; Convert the number into floating point format with base of exponent 10. ;; PRINTFP_MUL10_LOOP l084dh: push af ;084d Store AF to stack ld hl,l002ch ;084e Load FP_ONE_OVER_TEN address into HL call l0b05h ;0851 Call FP_COMPARE_HL jr nc,l0863h ;0854 If number printed is greater or equal to 0.1, ; jump to next step. call l0ae3h ;0856 Multiply number by 10 (call FP_MUL_10) pop af ;0859 Restore AF from stack dec a ;085a Decrement A jr l084dh ;085b Jump to PRINTFP_MUL10_LOOP ;; PRINTFP_DIV10_LOOP l085dh: call l0af4h ;085d Divide number by 10 (call FP_DIV_10) pop af ;0860 Restore AF from stack inc a ;0861 Increment A push af ;0862 Save AF to stack l0863h: ld hl,l00a0h ;0863 Load FP_ONE address into HL call l0b05h ;0866 Call FP_COMPARE_HL jr nc,l085dh ;0869 If number is greater or equal to 1, ; jump to PRINTFP_DIV10_LOOP ; M = abs( N * 10^(-1 - A) ) ; 1 > M >= 0.1 ; where N is the original number, and M is the number in HL' C' HL and at the ; top of the arithmetic stack. A is stored at the top of the arithmetic stack. ld a,(ix-2) ;086b Load negative exponent (base 2) into A neg ;086e ; Apply base 2 exponent to the number's mantissa ;; PRINTFP_APPLY_EXP2 l0870h: jr z,l087dh ;0870 Break from loop if exponent is zero. exx ;0872 srl c ;0873 Shift mantissa right (divide by 2) rr h ;0875 rr l ;0877 exx ;0879 dec a ;087a Decrement exponent jr l0870h ;087b Loop to PRINTFP_APPLY_EXP2 ; M = O * 2^(-24) ; where O is the number in HL' C' ; Build 8 digit binary-coded decimal at the top of the arithmetic stack l087dh: ld b,7 ;087d B = 7 push ix ;087f HL = IX (top of arithmetic stack) pop hl ;0881 ld (hl),000h ;0882 Load 0 into first BCD digit. inc hl ;0884 Increment HL ; This loop multiplies O by 10 in each iteration and stores the part of O ; that is before decimal point into (HL) ;; PRINTFP_BCD_LOOP l0885h: xor a ;0885 Clear A call l024fh ;0886 Call FP_MUL_10_ADD_A exx ;0889 ld a,b ;088a exx ;088b ld (hl),a ;088c Load B' (mantissa overflow) into HL inc hl ;088d Increment HL djnz l0885h ;088e Loop 7 times to PRINTFP_BCD_LOOP ; HL -8 -7 -6 -5 -4 -3 -2 -1 ; ------------------------------------------------------------------- ; 0 X X X X X X Y ; <-- most significant least significant --> ; P = M * 10^7 = abs( N * 10^(6 - A) ) ; Round the lowest X digit up, if Y digit >= 5. ld bc,l0600h ;0890 B = 6 ; C = 0 dec hl ;0893 Decrement HL ld a,(hl) ;0894 cp 5 ;0895 Compare last digit with 5 ; Propagate carry and also set the bitmap in C ;; PRINTFP_ROUND_LOOP l0897h: ccf ;0897 Cf set if >=5, reset if <5 ; Cf set if carry from the previous decimal digit. ld a,0 ;0898 Add 1 to the digit one place higher if Cf set. dec hl ;089a adc a,(hl) ;089b sla c ;089c Shift C left cp 10 ;089e Compare corrected digit with 10 jr c,l08a4h ;08a0 ld a,000h ;08a2 Load 0 into A if greater or equal to 10 l08a4h: ld (hl),a ;08a4 Load corrected digit into (HL) push af ;08a5 Save Cf on stack and a ;08a6 jr z,l08abh ;08a7 If digit not zero, set bit 0 of C. set 0,c ;08a9 l08abh: pop af ;08ab Restore Cf from stack djnz l0897h ;08ac Loop 6 times to PRINTFP_ROUND_LOOP ld a,c ;08ae Load bitmask into A pop bc ;08af Restore exponent from stack into B jr c,l08b8h ;08b0 If no carry on the most significant digit, ; break from loop... inc b ;08b2 ...else increment base 10 exponent push bc ;08b3 Save exponent back to stack ld b,001h ;08b4 jr l0897h ;08b6 loop one more time to PRINTFP_ROUND_LOOP ; P = abs( N * 10^(6 - A) ) l08b8h: ld c,a ;08b8 Move bitmask back to C ld a,b ;08b9 Move exponent back to A inc a ;08ba Jump to PRINTFP_EXP if A not between 0 and 6 jp m,l08c8h ;08bb cp 7 ;08be jr nc,l08c8h ;08c0 ; No exponential notation necessary. Just print the mantissa and exit. ld b,a ;08c2 Exponent to B call l091ch ;08c3 Call PRINT_BCD jr l090bh ;08c6 Finish (remove stored values from stack, and ; return) ; Number either too large or too small. Print in exponential notation. ;; PRINTFP_EXP l08c8h: push bc ;08c8 Save exponent and bitmask to stack ld b,001h ;08c9 Print mantissa call l091ch ;08cb (call PRINT_BCD) ld a,'E' ;08ce Print exponent character 'E' rst 20h ;08d0 ; Convert exponent into decimal and print it. pop bc ;08d1 Restore BC from stack bit 7,b ;08d2 Check sign bit of the exponent ld a,'+' ;08d4 jr z,l08e0h ;08d6 If exponent positive, jump to PRINTFP_EXP_POS ; Exponent base 10 is negative ld a,'-' ;08d8 Print minus sign rst 20h ;08da (call PUTCH_RST) ld a,b ;08db Load negative exponent base 10 into A neg ;08dc jr l08e2h ;08de Jump to PRINTFP_EXP_ABS ;; PRINTFP_EXP_POS l08e0h: rst 20h ;08e0 Print plus sign (call PUTCH_RST) ld a,b ;08e1 Load exponent base 10 into A ; Absolute value of exponent base 10 in A. Now split exponent into two decimal ; digits: B contains tens (+ ASCII offset), A contains ones. ;; PRINTFP_EXP_ABS l08e2h: ld b,'0' ;08e2 Initialize B l08e4h: cp 10 ;08e4 jr c,l0904h ;08e6 Jump to PRINTFP_END if number of ones is less ; than 10. add a,-10 ;08e8 Subtract 10 from number of ones and add 1 to inc b ;08ea the number of tens. jr l08e4h ;08eb Loop ; 'PRINT A 16 BIT NUMBER' ; ======================= ; Prints a 16 bit number at DE to the screen at current cursor position. ; Parameters: ; DE: Pointer to the 16 bit number to print. ; Returns: ; DE: DE + 2 ;; PRINT_WORD l08edh: ld a,(de) ;08ed Load BASIC line number (at DE) into HL and ld l,a ;08ee DE = DE + 2 inc de ;08ef ld a,(de) ;08f0 ld h,a ;08f1 inc de ;08f2 call l0abch ;08f3 Push line number to arithmetic stack ; (Call INT_TO_FP) ; 'PRINT A FLOATING POINT NUMBER' ; =============================== ; Prints a floating point number on the top of the arithmetic stack at the ; current cursor position. ; IX Offset -5 -4 -3 -2 -1 ; --------------------------------------------------- ; 24 bit mantissa (MSB) Exp Sign ;; PRINTFP l08f6h: push de ;08f6 Save BC,DE and HL to stack push bc ;08f7 push hl ;08f8 ld a,(ix-2) ;08f9 cp 080h ;08fc jp nz,l083dh ;08fe Jump to PRINTFP_DO if line number is not 0 xor a ;0901 Clear A ld b,020h ;0902 Load ASCII ' ' into B ;; PRINTFP_END l0904h: or 030h ;0904 Convert integer in A to ASCII numeral ld c,a ;0906 and store it into C ld a,b ;0907 Print character in B rst 20h ;0908 (Call PUTCH_RST) ld a,c ;0909 Print character in C rst 20h ;090a (Call PUTCH_RST) l090bh: pop hl ;090b Restore HL and BC from stack pop bc ;090c ; 'STORE ARITHMETIC RESULT & RETURN (cont.)' ; ========================================== ; Restores DE register from CPU stack, removes the top value from the ; arithmetic stack and returns. ;; STORE_ARITHM_RET_2 l090dh: pop de ;090d ; 'SUB IX,5' ; ========== ; Subtracts 5 from the contents of the IX register. Stores -5 in BC. ;; SUB_IX_5 l090eh: ld bc,0fffbh ;090e Load -5 into BC ; ADD_IX_10 jumps here l0911h: add ix,bc ;0911 Add BC to IX ret ;0913 Return ; 'CORRECT EXPONENT & ADD IX,10' ; ============================== ; Corrects exponent of FP number in HL C' HL' and adds 10 to IX ( ;; CORRECT_EXP_ADD_IX_10 l0914h: call l0c4ch ;0914 Call CORRECT_EXP ; 'ADD IX,10' ; =========== ; Adds 10 to the contents of the IX register. Stores 10 in BC. ;; ADD_IX_10 l0917h: ld bc,0000ah ;0917 Load 10 into BC jr l0911h ;091a Jump forward ; 'PRINT A BINARY-CODED DECIMAL NUMBER' ; ===================================== ; Prints a BCD number at HL. Each byte represents one digit. B holds the number ; of digits before decimal point. ; Parameters: ; HL: Pointer to the BCD number. ; B: Location of the decimal point (after B digits) ; C: Bitmap of the digits (0 means zero digit, 1 means non-zero digit) ; BCD digit HL+7 HL+6 HL+5 HL+4 HL+3 HL+2 HL+1 HL ; C bit 7 6 5 4 3 2 1 0 ;; PRINT_BCD l091ch: inc b ;091c ;; PRINT_BCD_LOOP l091dh: djnz l0922h ;091d Decrement B. ld a,'.' ;091f Print decimal point if B == 0 rst 20h ;0921 (call PUTCH_RST) l0922h: ld a,(hl) ;0922 Print BCD digit in (HL) or 030h ;0923 rst 20h ;0925 (call PUTCH_RST) inc hl ;0926 Move HL to the next digit. srl c ;0927 Shift C right and... jr nz,l091dh ;0929 ...loop to PRINT_BCD_LOOP if there are further ; non-zero digits ; There are only zeroes left. Note: B remains unmodified. dec b ;092b dec b ;092c ret m ;092d Return if B <= 1 inc b ;092e jr l091ch ;092f Restart to PRINT_BCD. ; 'PRINT A BASIC LINE TO SCREEN' ; ============================== ; Print a complete BASIC line to screen. First the line number is printed, ; followed by ASCII space and line contents. ; Parameters: ; DE: Pointer to the BASIC line. ;; PRINT_BASIC_LINE l0931h: call l08edh ;0931 Print line number (call PRINT_WORD) ld a,020h ;0934 Load ASCII ' ' into A ; 'PRINT A CHARACTER FOLLOWED BY A STRING TO SCREEN' ; ================================================== ; Call PUTCH_RST and PRINTSTR ;; PUTCH_PRINTSTR l0936h: rst 20h ;0936 Call PUTCH_RST ; 'PRINT A STRING TO SCREEN' ; ========================== ; Print a string of characters at (DE) to the screen at the current cursor ; position (CURSOR_POS). ; String must be terminated with 00h (NUL) or 0d (CR). In the second case a ; new line added at the end of the string. ; Parameters: ; DE: Address of the first character in the string. ; Returns: ; Zf: Set if string ended with NUL, reset if it ended with CR. ; HL': Address of the last printed character on screen + 1 ; DE: Address of the last read character + 1 ; Destroys: ; A,B,flags ;; PRINTSTR l0937h: xor a ;0937 A = ASCII NUL ; 'PRINT A STRING TO SCREEN ENDING ON A' ; ====================================== ; Like PRINTSTR, except string is terminated with character in A (which is not ; printed) or CR (which is printed). ; Returns: ; Zf: Set if string ended with character in A, reset if it ended with CR. ;; PRINTSTR_A l0938h: ld b,a ;0938 Load A into B l0939h: ld a,(de) ;0939 Load character pointed by DE into A inc de ;093a Increment DE cp b ;093b Compare A with B... ret z ;093c ...and return if equal rst 20h ;093d Call PUTCH_RST cp 00dh ;093e Compare A with 0dh (CR)... jr nz,l0939h ;0940 ...and loop if not equal inc a ;0942 Increment A ret ;0943 Return. ; 'MOVE MEMORY BLOCK' ; =================== ; ; ------------- ------------- ; |source | |destination| ; ------------- ------------- ; ^ ^ ^ ; | | | ; DE HL BC ;; MOVE_MEM l0944h: rst 10h ;0944 Call CMP_HL_DE ret z ;0945 Return if HL == DE ld a,(de) ;0946 (BC) = (DE) ld (bc),a ;0947 inc de ;0948 Increment BC and DE inc bc ;0949 jr l0944h ;094a Loop l094ch: ld a,b ;094c 78 x sub d ;094d 92 jr nz,l0953h ;094e 20 03 ld a,c ;0950 79 y sub e ;0951 93 ret z ;0952 c8 l0953h: dec de ;0953 1b dec hl ;0954 2b + ld a,(de) ;0955 1a ld (hl),a ;0956 77 w jr l094ch ;0957 18 f3 l0959h: pop bc ;0959 c1 pop hl ;095a e1 ld (02aa1h),hl ;095b 22 a1 2a " * ld a,h ;095e 7c | or l ;095f b5 jr z,l0972h ;0960 28 10 ( pop hl ;0962 e1 ld (02a91h),hl ;0963 22 91 2a " * pop hl ;0966 e1 ld (02a6eh),hl ;0967 22 6e 2a " n * pop hl ;096a e1 ld (02a93h),hl ;096b 22 93 2a " * pop hl ;096e e1 ld (02a95h),hl ;096f 22 95 2a " * l0972h: push bc ;0972 c5 ret ;0973 c9 l0974h: ld hl,-02b38h ;0974 pop bc ;0977 Remove BC from stack add hl,sp ;0978 jp nc,l0153h ;0979 Jump to SORRY_RST_PUSH_DE if SP < 2b38h ld hl,(02aa1h) ;097c 2a a1 2a * * ld a,h ;097f 7c | or l ;0980 b5 jr z,l0996h ;0981 28 13 ( ld hl,(02a95h) ;0983 2a 95 2a * * push hl ;0986 e5 ld hl,(02a93h) ;0987 2a 93 2a * * push hl ;098a e5 ld hl,(02a6eh) ;098b 2a 6e 2a * n * push hl ;098e e5 ld hl,(02a91h) ;098f 2a 91 2a * * push hl ;0992 e5 ld hl,(02aa1h) ;0993 2a a1 2a * * l0996h: push hl ;0996 e5 push bc ;0997 c5 ret ;0998 c9 ; 'EVALUATE RELATIONAL OPERATORS' ; =============================== ; Evaluates a relational operator at DE and returns HL = 1 if true or HL = 0 ; if false. ; If a relational expression is not found, it returns two functions up. ; Returns: ; HL: Result of the relational operator. ;; EVAL_RELATIONAL l0999h: rst 18h ;0999 Call READ_PAR db '>' ;099a db l09a3h-$-1 ;099b call l0439h ;099c Call EVAL_RELATIONAL_2 ret z ;099f Return HL = 1 if greater ret c ;09a0 inc hl ;09a1 ret ;09a2 l09a3h: rst 18h ;09a3 Call READ_PAR db '=' ;09a4 db l09ach-$-1 ;09a5 call l0439h ;09a6 Call EVAL_RELATIONAL_2 ret nz ;09a9 Return HL = 1 if equal inc hl ;09aa ret ;09ab l09ach: rst 18h ;09ac Call READ_PAR db '<' ;09ad db l0a02h-$-1 ;09ae Return two functions up. call l0439h ;09af Call EVAL_RELATIONAL_2 ret nc ;09b2 Return HL = 1 if less inc hl ;09b3 ret ;09b4 ; 'PUT CHARACTER ON SCREEN' ; ========================= ; Print a character in A to the screen at the current cursor position ; (CURSOR_POS). ; This function holds Galaksija's "terminal emulation" routines. Several ; special ASCII characters are recognized if C flag is set. Video output ; can be redirected with the use of VIDEO_LINK. ; Parameters: ; A: Character to print ; C flag: Character is considered special if C flag is set ; Returns: ; HL: Position of the printed character + 1 ; Destroys: ; BC,DE ;; PUTCH l09b5h: push af ;09b5 Push AF to stack call 02bach ;09b6 Call VIDEO_LINK ld hl,(02a68h) ;09b9 Load CURSOR_POS into HL jr c,l0a04h ;09bc If carry set, jump to SPECIAL_CHAR ld (hl),a ;09be Write character in A to screen inc hl ;09bf Increment HL ; Scroll the screen if necessary ;; PUTCH_SCROLL l09c0h: ld a,02ah ;09c0 Load 2ah into A cp h ;09c2 Compare with H jr nz,l09ffh ;09c3 If cursor is still within video memory, ; jump to PUTCH_END. ; Scroll screen contents content up one line, skipping WINDOW_LEN characters ; on top. ld hl,02bb0h ;09c5 Load SCROLL_CNT address into HL call l0a3dh ;09c8 Call WAIT_INT (wait for soft scroll to finish) push hl ;09cb Push HL to stack inc hl ;09cc Increment HL (now points to SCROLL_FLAG) inc (hl) ;09cd Set SCROLL_FLAG call l0a3dh ;09ce Call WAIT_INT (sync to video refresh) or a ;09d1 ? ld de,(02a6ch) ;09d2 Load WINDOW_LEN into DE res 1,d ;09d6 Reset bit 1 of D ld hl,001e0h ;09d8 Load 480 into HL (32 characters * 15 lines) sbc hl,de ;09db Substract DE from HL jr z,l09edh ;09dd Jump forward if result is zero... jr c,l09edh ;09df ...or less ld b,h ;09e1 Load HL into BC ld c,l ;09e2 (number of characters moved) set 3,d ;09e3 D = D | 28h set 5,d ;09e5 ld hl,32 ;09e7 HL (source) = DE (dest) + 32 add hl,de ;09ea (one line of characters lower) ldir ;09eb Block transfer ; Soft scroll screen up one line, if WINDOW_LEN is zero l09edh: ld hl,(02a6ch) ;09ed Load WINDOW_LEN into HL ld a,h ;09f0 Load top byte of WINDOW_LEN into A or l ;09f1 A = A | L pop hl ;09f2 Pop HL from stack (HL points to SCROLL_CNT) jr nz,l09f7h ;09f3 If WINDOW_LEN is not zero, jump forward... ld (hl),003h ;09f5 Else load 3 into SCROLL_CNT ; Clear bottom line l09f7h: ld hl,029e0h ;09f7 Load 29e0h into HL (start of the bottom line ; on the screen) push hl ;09fa Push HL on stack call l0a34h ;09fb Call CLEAR_LINE pop hl ;09fe Pop HL from stack ; Update cursor position and return. ;; PUTCH_END l09ffh: ld (02a68h),hl ;09ff Update cursor position l0a02h: pop af ;0a02 Pop AF from stack ret ;0a03 Return ; Interpret special characters ;; SPECIAL_CHAR l0a04h: cp 00dh ;0a04 Compare A with 0dh (ASCII CR) jr nz,l0a16h ;0a06 If not equal jump forward ; Put cursor on the next line ld a,h ;0a08 Load A with H cp 02bh ;0a09 Compare H with 2bh (is cursor on screen?) jr c,l0a11h ;0a0b If H less than 2bh jump forward... ld (hl),00dh ;0a0d ...else put character on screen jr l09ffh ;0a0f and jump to PUTCH_END l0a11h: call l0a34h ;0a11 Call CLEAR_LINE (puts cursor on the start ; of the next line) jr l09c0h ;0a14 Jump to PUTCH_SCROLL l0a16h: cp 00ch ;0a16 Compare A with 0ch (ASCII FF) jr nz,l0a27h ;0a18 If not equal jump forward ; Clear screen ld hl,029ffh ;0a1a Load 29ffh to HL (last character on screen) l0a1dh: ld (hl),020h ;0a1d Load 20h (ASCII space) to (HL) dec hl ;0a1f Decrement HL bit 1,h ;0a20 Test bit 1 of H jr z,l0a1dh ;0a22 If zero, loop l0a24h: inc hl ;0a24 Increment HL ; (points to the first char on screen) jr l09ffh ;0a25 Jump to PUTCH_SCROLL l0a27h: cp 01dh ;0a27 Compare A with 1dh (ASCII GS) jr nz,l09ffh ;0a29 If not equal jump to PUTCH_END ; Clear current character and move cursor one position back ld (hl),020h ;0a2b Load 20h (ASCII space) to (HL) dec hl ;0a2d Decrement HL bit 1,h ;0a2e Test bit 1 of H jr nz,l0a24h ;0a30 If not zero (cursor went off screen), jump ; to increment back HL. jr l09ffh ;0a32 Jump to PUTCH_END ; 'CLEAR LINE' ; ============ ; This function fills a line with ASCII space (20h) characters from the ; location pointed by HL to the end of the line. ; Parameters: ; HL: Pointer to the start of the line to be filled. ; Returns: ; HL: Pointer to the start of the next line ; Destroys: ; A, flags ;; CLEAR_LINE l0a34h: ld (hl),020h ;0a34 Store ASCII space at location pointed to by HL inc hl ;0a36 Increment HL ld a,l ;0a37 Load L into A and 01fh ;0a38 A = A & 1fh jr nz,l0a34h ;0a3a If A not zero, loop... ret ;0a3c ...else return ; 'WAIT FOR INTERRUPT' ; ==================== ; This function waits for an interrupt routine to reset a flag pointed to ; by HL register. ; Function returns immediately if interrupts are disabled. ; Parameters: ; HL: pointer to the flag (interrupt must set this to 0) ; Destroys: ; A, flags ;; WAIT_INT l0a3dh: ld a,i ;0a3d Load FF2 state into P/V flag ret po ;0a3f Return if interrupts are disabled l0a40h: ld a,(hl) ;0a40 Load flag byte into A or a ;0a41 Update Z flag jr nz,l0a40h ;0a42 If byte is not zero, loop... ret ;0a44 ...else return ; 'PUSH FLOATING POINT NUMBER ON STACK' ; ===================================== ; Stores a 4-byte floating point number at (HL) on the arithmetic stack. ; IX Offset 0 1 2 3 4 ; --------------------------------------------------- ; 24 bit mantissa (MSB) Exp Sign ; IX Offset 0 1 2 3 4 ; ------------------------------------------------------------- ; MMMMMMMM MMMMMMMM 1MMMMMMM EEEEEEEE S0000000 ; (LSB) (MSB) ; HL Offset 0 1 2 3 ; -------------------------------------------------- ; MMMMMMMM MMMMMMMM EMMMMMMM SEEEEEEE ; (LSB) (MSB) ;; PUSH_FP l0a45h: push de ;0a45 Save DE, HL and AF on stack push hl ;0a46 push af ;0a47 ld bc,00004h ;0a48 Load 4 into BC push ix ;0a4b Load IX into DE pop de ;0a4d ldir ;0a4e Transfer 4 bytes starting from (HL) into (IX) rl (ix+2) ;0a50 Extract sign bit from IX+2 and IX+3 rl (ix+3) ;0a54 ld a,b ;0a58 Store sign bit in IX+4 rra ;0a59 ld (ix+4),a ;0a5a scf ;0a5d Set top bit of mantissa rr (ix+2) ;0a5e ld c,005h ;0a62 IX = IX + 5 add ix,bc ;0a64 pop af ;0a66 Restore DE, HL and AL from stack pop hl ;0a67 pop de ;0a68 ret ;0a69 Return ; 'EVALULATE INTEGER VALUE IN PARENTHESIS' ; ======================================== ; Evaluates expression in parenthesis at DE, converts it to integer and returns ; it in DE. ;; EVAL_PAREN_INT l0a6ah: call l0781h ;0a6a Call EVAL_PAREN ; 'FLOATING POINT TO INTEGER' ; =========================== ; Convert floating point at the top of the arithmetic stack to integer. ; Returns: ; HL: Integer value ;; FP_TO_INT l0a6dh: exx ;0a6d call l090eh ;0a6e Call SUB_IX_5 ld de,l0000h ;0a71 Clear DE' ; IX Offset 0 1 2 3 4 ; --------------------------------------------------- ; 24 bit mantissa (MSB) Exp Sign ld a,(ix+3) ;0a74 Load exponent into A ld c,(ix+4) ;0a77 Load sign into C' cp 080h ;0a7a Return 0 if exponent equal to -128 jr z,l0aaah ;0a7c cp 001h ;0a7e Return 0 if exponent less than 1 jp m,l0aaeh ;0a80 (why not jump to l0aaah like above?) cp 010h ;0a83 Jump to error if exponent greater than 16 exx ;0a85 jp p,l065ah ;0a86 exx ;0a89 ; DE = mantissa * 2 ^ exponent ld b,a ;0a8a Load exponent into B' ld a,(ix+000h) ;0a8b Load mantissa into HL' A' ld l,(ix+001h) ;0a8e ld h,(ix+002h) ;0a91 l0a94h: sla a ;0a94 Shift left HL' A' adc hl,hl ;0a96 rl e ;0a98 Shift left DE' rl d ;0a9a djnz l0a94h ;0a9c Loop l0a9eh: sla c ;0a9e Jump forward if sign is positive jr nc,l0aaah ;0aa0 or h ;0aa2 Increment DE' if HL' A' not zero or l ;0aa3 jr z,l0aa7h ;0aa4 inc de ;0aa6 l0aa7h: call l0ad7h ;0aa7 DE' = -DE' (Call NEG_DE) l0aaah: push de ;0aaa Load result into HL exx ;0aab pop hl ;0aac ret ;0aad Return l0aaeh: ld a,0ffh ;0aae Set A to -1 jr l0a9eh ;0ab0 ; 'EVALUATE NUMERIC EXPRESSION' ; ============================= ; Evaluate numeric expression at DE and push the result on the arithmetic ; stack. ;; EVAL_EXPRESSION l0ab2h: call l068eh ;0ab2 Call EVAL_ARITHMETIC call l0999h ;0ab5 Call EVAL_RELATIONAL ; Returns to the calling function, if no ; relational operator found. db 001h ;0ab8 Dummy ld bc,nn (skip the following instruction) ; 'PUSH 10 TO ARITHMETIC STACK' ; ============================ ; Pushes floating point constant 10 to the arithmetic stack. ;; PUSH_FP_10 l0ab9h: ld hl,0000ah ;0ab9 ; Push result from EVAL_RELATIONAL to stack. ; 'INTEGER TO FLOATING POINT' ; =========================== ; Converts integer in HL to floating point and stores it into the arithmetic ; stack. ; Parameters: ; HL: Integer to convert. ;; INT_TO_FP l0abch: push de ;0abc Save DE on stack. ;; INT_TO_FP_2 l0abdh: ex de,hl ;0abd DE = integer to convert call l0917h ;0abe Call ADD_IX_10 call l0ad4h ;0ac1 Call ABS_DE push de ;0ac4 Save DE on stack ld hl,0010h ;0ac5 Load 16 into L rr h ;0ac8 Set MSB of H to sign bit. exx ;0aca pop de ;0acb Restore DE' from stack rst 28h ;0acc Clear HL' (Call CLEAR_HL) ld h,e ;0acd Load E' into H' ld c,d ;0ace Load D' into C' ; IX Offset -5 -4 -3 -2 -1 ; --------------------------------------------------- ; L' H' C' L H ; --------------------------------------------------- ; 24 bit mantissa (MSB) Exp Sign call l0c4ch ;0acf Call CORRECT_EXP jr l0b03h ;0ad2 Jump to STORE_ARITHM_RET_0 ; 'ABSOLUTE VALUE DE' ; =================== ; Calculates absolute value of integer in DE. ; Parameters: ; DE ; Returns: ; DE: Absolute value. ; Cf: Set if DE was negative, reset otherwise. ; Destroys: ; A ;; ABS_DE l0ad4h: xor a ;0ad4 Clear A add a,d ;0ad5 Add D to A. ret p ;0ad6 Return if D positive, otherwise continue to ; NEG_DE ; 'NEGATE DE' ; =========== ; Negates the value of integer in DE. ; Parameters: ; DE ; Returns: ; DE: Absolute value. ; Cf: Always Set. ; Destroys: ; A ;; NEG_DE l0ad7h: ld a,e ;0ad7 E = -E neg ;0ad8 ld e,a ;0ada ld a,d ;0adb D = ~D + ~Cf cpl ;0adc ccf ;0add adc a,000h ;0ade ld d,a ;0ae0 scf ;0ae1 Set carry flag ret ;0ae2 Return ; 'MULTIPLY BY 10' ; ================ ; Multiplies the number on the top of the arithmetic stack by 10 ;; FP_MUL_10 l0ae3h: call l0ab9h ;0ae3 Push 10 on the arithmetic stack ; (call PUSH_FP_10) ;; FP_MUL l0ae6h: call l0c68h ;0ae6 Call FETCH_TWO_FP jr z,l0b29h ;0ae9 If first operand is zero, just remove the ; second operand from stack and return (jumps ; to STORE_ARITHM_RET_2) cp e ;0aeb If the second operand is zero, store a zero jp z,l0b6bh ;0aec floating point value on stack and return ; (jump to STORE_ZERO_RET) call l0b81h ;0aef Call FP_MUL_DO jr l0b03h ;0af2 Jump to STORE_ARITHM_RET_0 ; 'DIVIDE BY 10' ; ================ ; Divides the number on the top of the arithmetic stack by 10 ;; FP_DIV_10 l0af4h: call l0ab9h ;0af4 Push 10 on the arithmetic stack ; (call PUSH_FP_10) ;; FP_DIV l0af7h: call l0c68h ;0af7 cd 68 0c h jr z,l0b29h ;0afa 28 2d ( - cp e ;0afc bb jp z,l065bh ;0afd ca 5b 06 [ call l0baeh ;0b00 cd ae 0b ; 'STORE ARITHMETIC RESULT TRAMPOLINE' ; ==================================== ;; STORE_ARITHM_RET_0 l0b03h: jr l0b6dh ;0b03 Jump to STORE_ARITHM_RET ; 'COMPARE VARIABLE WITH ARITHMETIC STACK' ; ======================================== ; Compares the number at the top of the arithmetic stack with the 4-byte ; floating point at (HL) ;; FP_COMPARE_HL l0b05h: call l0a45h ;0b05 Call PUSH_FP call l0c68h ;0b08 Call FETCH_TWO_FP ld bc,0fffbh ;0b0b Load -5 into BC jr l0b16h ;0b0e Jump into FP_COMPARE (remove the only the value ; pushed on the stach by PUSH_FP) ; 'COMPARE TWO FLOATING POINT NUMBERS' ; ==================================== ; Pops two floating point numbers from the top of the arithmetic stack, ; compares them and returns the result in the flags register. ;; FP_COMPARE l0b10h: call l0c68h ;0b10 Call FETCH_TWO_FP ld bc,0fff6h ;0b13 l0b16h: add ix,bc ;0b16 Subtract -10 from IX cp l ;0b18 Compare A (80h) with L call l0be6h ;0b19 Jump to the rest of the function pop de ;0b1c Restore DE ret ;0b1d Return ;; FP_SUB l0b1eh: call l0c68h ;0b1e cd 68 0c h jr nz,l0b28h ;0b21 20 05 call l0b62h ;0b23 cd 62 0b b jr l0b58h ;0b26 18 30 0 l0b28h: cp e ;0b28 bb l0b29h: jr z,l0b7eh ;0b29 Jump to STORE_ARITHM_RET_2 xor d ;0b2b aa ld d,a ;0b2c 57 W jr l0b3ah ;0b2d 18 0b call l0a45h ;0b2f cd 45 0a E ; 'ADD TWO FLOATING POINT NUMBERS' ; ================================ ; Pops two numbers from the top of the arithmetic stack, adds them and pushes ; the result on the stack. ; IX Offset -5 -4 -3 -2 -1 ; --------------------------------------------------- ; E' D' B' E D ; --------------------------------------------------- ; 24 bit mantissa (MSB) Exp Sign ; IX Offset -10 -9 -8 -7 -6 ; --------------------------------------------------- ; L' H' C' L H ; --------------------------------------------------- ; 24 bit mantissa (MSB) Exp Sign ;; FP_ADD l0b32h: call l0c68h ;0b32 Call FETCH_TWO_FP jr z,l0b63h ;0b35 Second number is zero. cp e ;0b37 jr z,l0b7eh ;0b38 First number is zero. Just remove the second ; operand from stack. ; Jump to STORE_ARITHM_RET_2 l0b3ah: call l0c04h ;0b3a Call FP_COMPARE_ABS jr z,l0b4dh ;0b3d Mantissa and exponent are equal jr nc,l0b48h ;0b3f If DE' B' DE > HL' C' HL, exchange operands ex de,hl ;0b41 exchange DE' B' DE and HL' C' HL exx ;0b42 ex de,hl ;0b43 ld a,c ;0b44 ld c,b ;0b45 ld b,a ;0b46 exx ;0b47 l0b48h: call l0c17h ;0b48 Call FP_ADD_DO jr l0b6dh ;0b4b Jump to STORE_ARITHM_RET ; Mantissa and exponent of operands are equal l0b4dh: ld a,h ;0b4d A = XOR of both sign bits xor d ;0b4e jr nz,l0b6bh ;0b4f Signs different: jump to STORE_ZERO_RET ld e,001h ;0b51 Signs equal: Multiply by 2 call l0c3fh ;0b53 Jump to FP_MUL_EXP jr l0b6dh ;0b56 Jump to STORE_ARITHM_RET l0b58h: ld a,(ix-1) ;0b58 dd 7e ff ~ xor 080h ;0b5b ee 80 ld (ix-1),a ;0b5d dd 77 ff w pop de ;0b60 d1 ret ;0b61 c9 l0b62h: push de ;0b62 d5 ; Second operand of addition is zero. ;; FP_ADD_ZERO_2 l0b63h: ld h,d ;0b63 HL' C' HL = DE' B' DE ld l,e ;0b64 exx ;0b65 ld l,e ;0b66 ld h,d ;0b67 ld c,b ;0b68 exx ;0b69 db 001h ;0b6a Dummy ld bc,nn (skip ld l,080h) ; 'STORE ZERO RESULT & RETURN' ; ============================ ;; STORE_ZERO_RET l0b6bh: ld l,080h ;0b6b ; 'STORE ARITHMETIC RESULT & RETURN' ; ================================== ; JP to this location is used by arithmetic functions with two operands to ; store the result on the arithmetic stack at IX. ; DE pointer is restored from CPU stack before returning. ; Top two values are removed from the stack and the Floating point number in ; HL' C' HL is stored. ; IX Offset -5 -4 -3 -2 -1 ; --------------------------------------------------- ; L' H' C' L H ; --------------------------------------------------- ; 24 bit mantissa (MSB) Exp Sign ;; STORE_ARITHM_RET l0b6dh: ld (ix-6),h ;0b6d Store result in the second-to-last ld (ix-7),l ;0b70 stack location. exx ;0b73 ld (ix-10),l ;0b74 ld (ix-9),h ;0b77 ld (ix-8),c ;0b7a exx ;0b7d l0b7eh: jp l090dh ;0b7e Jump to STORE_ARITHM_RET_2 ; 'MULTIPLY TWO FLOATING POINT NUMBERS (cont.)' ; ============================================= ; Multiplies two floating point numbers (both not equal to zero). ; (first operand, result) ; IX Offset -5 -4 -3 -2 -1 ; --------------------------------------------------- ; E' D' B' E D ; --------------------------------------------------- ; 24 bit mantissa (MSB) Exp Sign ; (second operand) ; IX Offset -10 -9 -8 -7 -6 ; --------------------------------------------------- ; L' H' C' L H ; --------------------------------------------------- ; 24 bit mantissa (MSB) Exp Sign ;; FP_MUL_DO l0b81h: ld a,h ;0b81 Sign bit of the result = XOR sign bits of xor d ;0b82 operands. ld h,a ;0b83 dec e ;0b84 Decrement first exponent. push hl ;0b85 Save HL and BC on stack push bc ;0b86 ld b,018h ;0b87 06 18 call l0c81h ;0b89 cd 81 0c xor a ;0b8c af rst 28h ;0b8d Call CLEAR_HL ld c,a ;0b8e 4f O l0b8fh: exx ;0b8f d9 srl c ;0b90 cb 39 9 rr h ;0b92 cb 1c rr l ;0b94 cb 1d exx ;0b96 d9 jr nc,l0b9dh ;0b97 30 04 0 add hl,de ;0b99 19 ld a,c ;0b9a 79 y adc a,b ;0b9b 88 ld c,a ;0b9c 4f O l0b9dh: exx ;0b9d d9 djnz l0ba5h ;0b9e 10 05 pop bc ;0ba0 c1 pop hl ;0ba1 e1 exx ;0ba2 d9 jr l0bd5h ;0ba3 18 30 0 l0ba5h: exx ;0ba5 d9 rr c ;0ba6 cb 19 rr h ;0ba8 cb 1c rr l ;0baa cb 1d jr l0b8fh ;0bac 18 e1 l0baeh: ld a,e ;0bae 7b { neg ;0baf ed 44 D ld e,a ;0bb1 5f _ ld a,h ;0bb2 7c | xor d ;0bb3 aa ld h,a ;0bb4 67 g push hl ;0bb5 e5 push bc ;0bb6 c5 ld b,019h ;0bb7 06 19 exx ;0bb9 d9 l0bbah: sbc hl,de ;0bba ed 52 R ld a,c ;0bbc 79 y sbc a,b ;0bbd 98 ld c,a ;0bbe 4f O jr nc,l0bc4h ;0bbf 30 03 0 add hl,de ;0bc1 19 adc a,b ;0bc2 88 ld c,a ;0bc3 4f O l0bc4h: exx ;0bc4 d9 ccf ;0bc5 3f ? adc hl,hl ;0bc6 ed 6a j rl c ;0bc8 cb 11 djnz l0bd7h ;0bca 10 0b push hl ;0bcc e5 push bc ;0bcd c5 exx ;0bce d9 pop bc ;0bcf c1 pop hl ;0bd0 e1 exx ;0bd1 d9 pop bc ;0bd2 c1 pop hl ;0bd3 e1 exx ;0bd4 d9 l0bd5h: jr l0c35h ;0bd5 18 5e ^ l0bd7h: exx ;0bd7 d9 add hl,hl ;0bd8 29 ) rl c ;0bd9 cb 11 jr nc,l0bbah ;0bdb 30 dd 0 ccf ;0bdd 3f ? sbc hl,de ;0bde ed 52 R ld a,c ;0be0 79 y sbc a,b ;0be1 98 ld c,a ;0be2 4f O or a ;0be3 b7 jr l0bc4h ;0be4 18 de ; 'COMPARE TWO FLOATING POINT NUMBERS (cont.)' ; ============================================ ; E' D' B' E D ; --------------------------------------------------- ; 24 bit mantissa (MSB) Exp Sign ; L' H' C' L H ; --------------------------------------------------- ; 24 bit mantissa (MSB) Exp Sign ; signs A B - - - + + - + + ; |A| > |B| A < B A < B A > B A > B ; |A| = |B| A = B A < B A > B A = B ; |A| < |B| A > B A < B A > B A < B ;; FP_COMPARE_2 l0be6h: jr z,l0bf2h ;0be6 HL' C' HL == 0 cp e ;0be8 jr z,l0bfah ;0be9 DE' B' DE == 0 ld a,h ;0beb A = XOR of both sign bits xor d ;0bec (resets Cf) call z,l0c04h ;0bed Call FP_COMPARE_ABS if signs are equal ; Different signs: Cf = 0, Zf = 0 ; Equal signs: Cf Zf ; |HL' C' HL| > |DE' B' DE| : 0 0 ; |HL' C' HL| = |DE' B' DE| : 0 1 ; |HL' C' HL| < |DE' B' DE| : 1 0 jr l0bf9h ;0bf0 Jump forward l0bf2h: cp e ;0bf2 Return if both numbers are 0 ret z ;0bf3 ; HL' C' HL == 0 ; DE' B' DE != 0 scf ;0bf4 Cf Zf bit 7,d ;0bf5 DE' B' DE > 0 : 1 1 ; DE' B' DE < 0 : 1 0 jr l0bfch ;0bf7 l0bf9h: ret z ;0bf9 Return if sign, mantissa and exponent are equal ; also jumps here if DE' B' DE == 0 (Cf = 0) l0bfah: ; Zf bit 7,h ;0bfa HL' C' HL > 0 : 1 ; HL' C' HL < 0 : 0 ; Cf Zf Result Cf Zf ; ------------------------------------ ; 0 0 1 0 ; 0 1 0 1 ; 1 0 0 0 ; 1 1 1 1 l0bfch: ccf ;0bfc ret nz ;0bfd Return with complemented Cf if Zf = 0 ccf ;0bfe rra ;0bff A = A | 01h scf ;0c00 rl a ;0c01 Clear Zf ret ;0c03 Return with original Cf if Zf = 1 ; 'COMPARE ABSOLUTE VALUES' ; ========================= ; Compares mantissa and exponent of two floating point numbers. ; Parameters: ; DE' B' DE, HL' C' HL: Floating point numbers to compare ; Returns: ; Cf, Zf: Result of (HL' C' HL - DE' B' DE) comparisson. ; E' D' B' E D ; --------------------------------------------------- ; 24 bit mantissa (MSB) Exp Sign ; L' H' C' L H ; --------------------------------------------------- ; 24 bit mantissa (MSB) Exp Sign ;; FP_COMPARE_ABS l0c04h: ld a,l ;0c04 Subtract exponents sub e ;0c05 jr z,l0c0fh ;0c06 Equal exponents: Compare mantissa jp po,l0c0dh ;0c08 Negate top bit of result on overflow neg ;0c0b l0c0dh: rlca ;0c0d Shift top bit into Cf ret ;0c0e Return l0c0fh: exx ;0c0f ld a,c ;0c10 Compare top bytes of mantissa cp b ;0c11 jr nz,l0c15h ;0c12 rst 10h ;0c14 Compare lower two bytes if top bytes equal ; (Call CMP_HL_DE) l0c15h: exx ;0c15 ret ;0c16 Return ; 'ADD TWO FLOATING POINT NUMBERS (cont.)' ; ======================================== ; Adds number in DE' B' DE to HL' C' HL. HL' C' HL must be larger than ; DE' B' DE. ; IX Offset -5 -4 -3 -2 -1 ; --------------------------------------------------- ; E' D' B' E D ; --------------------------------------------------- ; 24 bit mantissa (MSB) Exp Sign ; IX Offset -10 -9 -8 -7 -6 ; --------------------------------------------------- ; L' H' C' L H ; --------------------------------------------------- ; 24 bit mantissa (MSB) Exp Sign ;; FP_ADD_DO l0c17h: ld a,l ;0c17 sub e ;0c18 A = L - E jr z,l0c29h ;0c19 Jump forward if exponents already equal cp 24 ;0c1b ret nc ;0c1d If L - E >= 24, return ; (DE' B' DE is too small to affect HL' C' HL) ; Make exponents equal exx ;0c1e l0c1fh: srl b ;0c1f Shift mantissa (DE' B') right rr d ;0c21 rr e ;0c23 dec a ;0c25 Loop A times jr nz,l0c1fh ;0c26 exx ;0c28 l0c29h: ld e,000h ;0c29 Clear ld a,h ;0c2b A = XOR both sign bits xor d ;0c2c jp m,l0c46h ;0c2d Signs different exx ;0c30 HL' C' = HL' C' + DE' B' add hl,de ;0c31 ld a,c ;0c32 adc a,b ;0c33 ld c,a ;0c34 l0c35h: jr nc,l0c3eh ;0c35 If carry, rotate mantissa right to rr c ;0c37 include the carry bit. rr h ;0c39 rr l ;0c3b scf ;0c3d Clear carry flag. l0c3eh: exx ;0c3e ; Multiply with Cf and return ; 'MULTIPLY FLOATING POINT NUMBER WITH 2^(E+Cf)' ; ============================================== ; Multiplies floating point number in HL' C' HL with 2^(E+Cf) ; Parameters: ; E, Cf: Number to multiply with ; HL' C' HL: Floating point number ; Return: ; HL' C' HL: Result ;; FP_MUL_EXP l0c3fh: ld a,l ;0c3f A = L + E + Cf adc a,e ;0c40 l0c41h: jp pe,l0c61h ;0c41 Jump to CORRECT_EXP_OVERFLOW if exponent ; overflowed... ld l,a ;0c44 ...else load A into L and return ret ;0c45 l0c46h: exx ;0c46 d9 sbc hl,de ;0c47 ed 52 R ld a,c ;0c49 79 y sbc a,b ;0c4a 98 ld c,a ;0c4b 4f O ; 'CORRECT EXPONENT' ; ================== ; This function removes any leading zeros in the mantissa and corrects ; the exponent. ; It is always called with the C' HL' register set selected. ; Parameters: ; HL C' HL': Input floating point number ; Returns: ; HL C' HL': Corrected floating point number. ;; CORRECT_EXP l0c4ch: ld b,018h ;0c4c Load 24 into B' xor a ;0c4e Clear A inc c ;0c4f Set S flag for C' dec c ;0c50 l0c51h: jp m,l0c5dh ;0c51 Break from loop top bit of C' HL' set dec a ;0c54 Decrement A add hl,hl ;0c55 Shift C' HL' left rl c ;0c56 djnz l0c51h ;0c58 Loop ; all 24 bits of C' HL' are zero. ;; CORRECT_EXP_ZERO l0c5ah: ld l,080h ;0c5a Set exponent to -128 ret ;0c5c Return l0c5dh: exx ;0c5d add a,l ;0c5e Add L to A jr l0c41h ;0c5f Check for overflow. ; Negative overflow in the exponent occured. ;; CORRECT_EXP_OVERFLOW l0c61h: ld a,h ;0c61 or a ;0c62 jp p,l0658h ;0c63 Jump to "HOW?" error if number was positive... jr l0c5ah ;0c66 ...else jump to CORRECT_EXP_ZERO ; 'FETCH TWO FLOATING POINT NUMBERS FROM STACK' ; ============================================= ; Fetches two floating point number from the top of the arithmetic stack and ; stores them HL' C' HL and DE' B' DE registers. Does not decrement IX. ; Returns: ; A: 80h ; DE' B' DE: First floating point number ; HL' C' HL: Second floating point number ; Zf: Set if second number is 0. ; IX Offset -5 -4 -3 -2 -1 ; --------------------------------------------------- ; E' D' B' E D ; --------------------------------------------------- ; 24 bit mantissa (MSB) Exp Sign ; IX Offset -10 -9 -8 -7 -6 ; --------------------------------------------------- ; L' H' C' L H ; --------------------------------------------------- ; 24 bit mantissa (MSB) Exp Sign ;; FETCH_TWO_FP l0c68h: pop hl ;0c68 push de ;0c69 Store DE and HL on stack push hl ;0c6a ld d,(ix-1) ;0c6b ld e,(ix-2) ;0c6e ld h,(ix-6) ;0c71 ld l,(ix-7) ;0c74 exx ;0c77 ld e,(ix-5) ;0c78 ld d,(ix-4) ;0c7b ld b,(ix-3) ;0c7e l0c81h: ld l,(ix-10) ;0c81 ld h,(ix-9) ;0c84 ld c,(ix-8) ;0c87 exx ;0c8a ld a,080h ;0c8b Is second number equal to zero? cp l ;0c8d ret ;0c8e Return ; 'RANDOM' ; ======== ; "RND" BASIC command. ; ; This pseudo-random number generator pushes a random floating point number ; on the arithmetic stack. ;; RND l0c8fh: push de ;0c8f Save DE on stack exx ;0c90 ld hl,02aa7h ;0c91 Load RND_SEED address into HL' push hl ;0c94 Save HL' on stack ld e,(hl) ;0c95 Load contents of RND_SEED into DE' B' inc hl ;0c96 ld d,(hl) ;0c97 inc hl ;0c98 ld b,(hl) ;0c99 exx ;0c9a ; Clear HL' C' HL call l0248h ;0c9b Call CLEAR_CX_HLX_B6 rst 28h ;0c9e Call CLEAR_HL ld c,003h ;0c9f Load 03h into C l0ca1h: ld b,008h ;0ca1 Load 8 into B ld d,(hl) ;0ca3 Load (HL) into D l0ca4h: exx ;0ca4 add hl,hl ;0ca5 Rotate HL' C' left rl c ;0ca6 exx ;0ca8 rl d ;0ca9 Rotate D left jr nc,l0cb3h ;0cab Add DE' B' if top bit of D is set... exx ;0cad add hl,de ;0cae HL' C' = HL' C' + DE' B' ld a,c ;0caf adc a,b ;0cb0 ld c,a ;0cb1 exx ;0cb2 l0cb3h: djnz l0ca4h ;0cb3 Loop 8 times inc hl ;0cb5 Increment HL dec c ;0cb6 Decrement C jr nz,l0ca1h ;0cb7 Loop 3 times rst 28h ;0cb9 Call CLEAR_HL exx ;0cba pop de ;0cbb Restore RND_SEED address from stack into DE' ld a,l ;0cbc L' = L' + 65h add a,065h ;0cbd (RND_SEED) = L' ld (de),a ;0cbf inc de ;0cc0 Increment DE' ld l,a ;0cc1 ld a,h ;0cc2 H' = H' + b0h + Cf adc a,0b0h ;0cc3 (RND_SEED+1) = H' ld (de),a ;0cc5 inc de ;0cc6 Increment DE' ld h,a ;0cc7 ld a,c ;0cc8 C' + 05h + Cf adc a,005h ;0cc9 (RND_SEED+2) = C' ld (de),a ;0ccb ld c,a ;0ccc call l0914h ;0ccd Call CORRECT_EXP_ADD_IX_10 jp l0b6dh ;0cd0 Jump to STORE_ARITHM_RET ; 'CONVERT STRING TO AN INTEGER' ; ============================== ; Converts string at DE into an integer. String must start with a numeral. ; Parameters: ; DE: Pointer to the string to be converted. ; Returns: ; HL: Converted integer or 0 on error. ; Zf: Set on error or clears on success. ; DE: First character after the converted number. ;; STRING_TO_INT l0cd3h: rst 28h ;0cd3 Call CLEAR_HL call l0105h ;0cd4 Call EAT_SPACE call l0172h ;0cd7 Check if the next character is a number ; (call CHAR_TO_INT) jr c,l0ce3h ;0cda If it's not, jump forward dec de ;0cdc Decrement DE (step back to character used for ; CHAR_TO_INT) ; Convert the string at DE to integer in HL. call l01a2h ;0cdd Call STRING_TO_FP call l0a6dh ;0ce0 Call FP_TO_INT l0ce3h: ld a,h ;0ce3 Check if HL contains 0 (either the number or l ;0ce4 read is 0 or the conversion failed) ret ;0ce5 Return ; 'READ A KEY FROM KEYBOARD' ; ========================== ; Waits for a key press on the keyboard and returns the code of the pressed ; key in A (for most keys this means the ASCII code of the character printed ; on the key. Special keys return their own scan code). ; A press to the "list" key causes a jump to the LIST function. ; A press to the "break" key causes a jump to the BREAK function. ; Returns: ; A: Code of the key pressed ; Destroys: ; Flags ; A key had been seen as released. Key code in A. Address in DE. ; Clear the key's position in KEY_DIFF (HL), if this key has been recently ; pressed. ;; KEY_RELEASED l0ce6h: cp h ;0ce6 Compare A with H... jr nz,l0cebh ;0ce7 ...and jump forward if not equal ld h,000h ;0ce9 ...else reset H l0cebh: cp l ;0ceb Compare A with L... jr nz,l0cf0h ;0cec ...and jump to KEY_READ_NEXT if not equal ld l,000h ;0cee ...else reset L ; Scan the next key, or start from the beginning if on the last key. ;; KEY_READ_NEXT l0cf0h: dec e ;0cf0 Decrement E, move to the next key jr nz,l0cfeh ;0cf1 Jump to KEY_READ if more keys to read jr l0cfbh ;0cf3 Jump to KEY_RESTART if on the last key. ; Entry point into the function ;; KEY l0cf5h: exx ;0cf5 Save BC, DE and HL registers ld hl,(02aa5h) ;0cf6 Load KEY_DIFF into HL ld c,00eh ;0cf9 Load 0eh into C (speed of the "repeat" key) ; Restart scanning from the beginning. ;; KEY_RESTART l0cfbh: ld de,02034h ;0cfb Load 2034h into DE ; This is the address of the last key on the ; keyboard except for "shift". ;; KEY_READ l0cfeh: ld a,(de) ;0cfe Load (DE) into A rrca ;0cff Rotate A right through carry flag ld a,e ;0d00 Load E (code of the current key) into A jr c,l0ce6h ;0d01 Jump to KEY_RELEASED if low bit of A is set. cp 032h ;0d03 Compare A with 32h... jr nz,l0d0fh ;0d05 ...and jump to KEY_PRESSED, if not equal ; "rept" key is pressed. This key is special because it causes the last ; keypress to be repeated. dec c ;0d07 Decrement C... jr nz,l0ce6h ;0d08 ...and jump to KEY_RELEASED if not zero ld a,(02bb4h) ;0d0a Load LAST_KEY into A jr l0d54h ;0d0d Jump to KEY_END ; A key had been seen as pressed. Key code in A. Address in DE. ;; KEY_PRESSED l0d0fh: cp h ;0d0f Compare A with H... jr z,l0cf0h ;0d10 ...and jump to KEY_READ_NEXT if equal cp l ;0d12 Compare A with L... jr z,l0cf0h ;0d13 ...and jump to KEY_READ_NEXT if equal ; Wait for 256 cycles for key release (to filter out switch bounces) ld b,000h ;0d15 Clear B l0d17h: rst 10h ;0d17 Call CMP_HL_DE (pause) ld a,(de) ;0d18 Load (DE) into A rrca ;0d19 Rotate A right through carry flag... jr c,l0ce6h ;0d1a ...and jump to KEY_RELEASED if carry set. djnz l0d17h ;0d1c Loop 256 times ; Fill in an empty position in KEY_DIFF (HL) ld a,h ;0d1e Load H into A or a ;0d1f Set Z flag... jr nz,l0d25h ;0d20 ...and jump forward if A not zero ld h,e ;0d22 Load E into H jr l0d2ah ;0d23 Jump forward l0d25h: ld a,l ;0d25 Load L into A or a ;0d26 Set Z flag... jr nz,l0cfeh ;0d27 ...and jump to KEY_READ if A not zero ld l,e ;0d29 Load E into L ; A valid key press has been detected. Check for special keys, add proper ; ASCII offsets and look keys up in the shift table. DE holds the key's ; memory address. ;; KEY_COMPUTE l0d2ah: ld (02aa5h),hl ;0d2a Load HL into KEY_DIFF rst 28h ;0d2d Call CLEAR_HL ld a,e ;0d2e Load E into A ; Check for special keys, that directly invoke functions in the BASIC ; interpreter. cp 034h ;0d2f Compare A with 34h ("list" key) and... jp z,l0461h ;0d31 ...jump to LIST_KEY if equal cp 031h ;0d34 Compare A with 31h ("break" key) and... jp z,l0305h ;0d36 ...jump to BREAK if equal cp 01bh ;0d39 Compare A with 1bh ("up" key). ld hl,02035h ;0d3b Load 2035h into HL ("shift" key address) jr c,l0d59h ;0d3e Jump to KEY_END_ALPHA if A less than 1bh cp 01fh ;0d40 Compare A with 1fh ("space" key address) jr c,l0d54h ;0d42 Jump to KEY_END if A is less than 1fh ; ("up", "down", "left" or "right" keys) ; Code for numerical and symbol keys, that are looked up in KEY_SHIFT_SYM_TABLE sub 01fh ;0d44 Subtract 1fh from A rr (hl) ;0d46 Rotate (HL) right ; Since HL contains the address of the "shift" ; key, this moves that status of the "shift" key ; into carry flag. rla ;0d48 Rotate A left. ; A = A * 2 + 0 (if shift is pressed) ; A = A * 2 + 1 (if shirt is released) ld c,a ;0d49 Load A into C ld hl,l0d70h ;0d4a Load KEY_SHIFT_SYM_TABLE address into HL add hl,bc ;0d4d Add BC to HL ; This seeds the random generator with the value of the R register after ; some key presses. ld a,r ;0d4e Load R into A ld (02aa8h),a ;0d50 Load A into 2nd byte of RND_SEED ld a,(hl) ;0d53 Load a character from the symbol table into A ; The code of the pressed key is in A. Store in LAST_KEY and return. ;; KEY_END l0d54h: ld (02bb4h),a ;0d54 Load A into LAST_KEY exx ;0d57 Restore BC, DE and HL registers. ret ;0d58 Return ; Pressed key is a letter. Add proper ASCII offset, check shift mapping in ; table and return. ;; KEY_END_ALPHA l0d59h: add a,040h ;0d59 Add 40h to A (ASCII offset) rr (hl) ;0d5b Rotate (HL) right... ; Since HL contains the address of the "shift" ; key, this moves that status of the "shift" key ; into carry flag. jr c,l0d54h ;0d5d ...and jump to KEY_END if shift key not pressed. ld hl,l0d94h ;0d5f Load KEY_SHIFT_YU_TABLE address into HL ld bc,l045bh ;0d62 Load 4 into B and 5bh into C ; B holds the length of the KEY_SHIFT_YU_TABLE, C holds the first ASCII code ; of the Yugoslavian character set. l0d65h: cp (hl) ;0d65 Compare (HL) with A... jr z,l0d6dh ;0d66 Jump forward if equal inc hl ;0d68 Increment HL inc c ;0d69 Increment C djnz l0d65h ;0d6a Loop 4 times ; If loops ends here, a dummy load instruction is executed. Else value in C ; (a different character) is stored into A db 00eh ;0d6c These two bytes also form the instruction l0d6dh: ld a,c ;0d6d "ld c,079h" jr l0d54h ;0d6e Jump to KEY_END ; 'KEYBOARD TABLES' ; ================= ; This table contains characters, that are obtained by pressing keys with ; addresses from 1fh ("space") to 30h ("return"). ; The first character in table is obtained by pressing shift-key and the ; second is obtained by pressing the key alone. ;; KEY_SHIFT_SYM_TABLE l0d70h: db ' ', ' ' ; 1fh db '_', '0' ; 20h db '!', '1' ; 21h db '"', '2' ; 22h db '#', '3' ; 23h db '$', '4' ; 24h db '%', '5' ; 25h db '&', '6' ; 26h db 0bfh, '7' ; 27h (bfh is block graphics character) db '(', '8' ; 28h db ')', '9' ; 29h db '+', ';' ; 2ah db '*', ':' ; 2bh db '<', ',' ; 2ch db '-', '=' ; 2dh db '>', '.' ; 2eh db '?', '/' ; 2fh db 00dh, 00dh ; 30h (CR) ; This table contains characters, that are mapped to Yugoslavian characters ; when the shift-key is pressed. ; It also serves as the KEY_SHIFT_SYM_TABLE entries for "break" and "repeat" ; keys, which have special functions and are never looked up here. ;; KEY_SHIFT_YU_TABLE l0d94h: db 'X' ; C with caron 31h db 'C' ; C with acute db 'Z' ; Z with caron 32h db 'S' ; S with caron ; KEY_SHIFT_SYM_TABLE entry for the "delete" key db 0ch, 00h ; 33h (clear screen - shift delete, delete) ; 'MEM' ; ===== ; "MEM" BASIC function ;; MEM l0d9ah: call l0183h ;0d9a Call FREE_MEM ld bc,(02a99h) ;0d9d Subtract array length from sbc hl,bc ;0da1 jr l0dbfh ;0da3 Jump to INT_TO_FP and return ; 'WORD' ; ====== ; "WORD" BASIC function ;; WORD l0da5h: ld c,(hl) ;0da5 Load word at (HL) into HL inc hl ;0da6 ld h,(hl) ;0da7 ld l,c ;0da8 jr l0dbfh ;0da9 Jump to INT_TO_FP and return. ; 'KEY' ; ===== ; "KEY" BASIC function ;; KEY_CMD l0dabh: ld a,h ;0dab Check if HL = 0 or l ;0dac jr nz,l0db4h ;0dad If HL = 0 (no argument), ; read which key has been pressed and return call l0cf5h ;0daf Call KEY jr l0dbah ;0db2 Jump to KEY_CMD_END l0db4h: set 5,h ;0db4 HL = HL | 2000h ld a,(hl) ;0db6 Load value from keyboard into A cpl ;0db7 Complement A l0db8h: and 001h ;0db8 Filter out LSB. ;; KEY_CMD_END l0dbah: ld l,a ;0dba Move A into L. db 03eh ;0dbb Dummy "ld a,nn" (skip next instruction - ; clear H and jump to INT_TO_FP) ; 'BYTE' ; ====== ; "BYTE" BASIC function ;; BYTE_FUNC l0dbch: ld l,(hl) ;0dbc Load byte at (HL) into HL ld h,000h ;0dbd l0dbfh: jp l0abch ;0dbf Jump to INT_TO_FP and return. ;; EQ l0dc2h: call l05fch ;0dc2 cd fc 05 push hl ;0dc5 e5 rst 18h ;0dc6 df inc l ;0dc7 2c , nop ;0dc8 00 call l05fch ;0dc9 cd fc 05 pop bc ;0dcc c1 l0dcdh: ld a,(bc) ;0dcd 0a cp (hl) ;0dce be jr nz,l0ddch ;0dcf 20 0b or a ;0dd1 b7 jr z,l0ddbh ;0dd2 28 07 ( inc hl ;0dd4 23 # inc bc ;0dd5 03 ld a,l ;0dd6 7d } and 00fh ;0dd7 e6 0f jr nz,l0dcdh ;0dd9 20 f2 ; following lines from Diss_010.jpg l0ddbh: db 03eh ;0ddb l0ddch: xor a ;0ddc af > jr l0db8h ;0ddd 18 d9 ;; HEX l0ddfh: call l0165h ;0ddf Test the first character ; (Call HEXCHAR_TO_INT_ jr c,l0df8h ;0de2 Jump to HOW_RST_PUSH_DE if conversion failed dec de ;0de4 Move DE back to the beginning rst 28h ;0de5 Call CLEAR_HL ;; HEX_LOOP l0de6h: call l0165h ;0de6 Call HEXCHAR_TO_INT jr c,l0dbfh ;0de9 Jump to INT_TO_FP and return on end of hex ; number (on first non-hex character) rlca ;0deb A = A << 4 rlca ;0dec rlca ;0ded rlca ;0dee ld bc,l0de6h ;0def Load HEX_LOOP address to the top of the push bc ;0df2 stack ; Shift four bits in A to HL l0df3h: ld b,004h ;0df3 l0df5h: rlca ;0df5 HL A = HL A << 4 adc hl,hl ;0df6 l0df8h: jp c,l065ah ;0df8 Jump to HOW_RST_PUSH_DE on overflow djnz l0df5h ;0dfb Loop ret ;0dfd Jump to HEX_LOOP ; 'WORD' ; ====== ; "WORD" BASIC command. l0dfeh: db 0f6h ;0dfe Dummy "or nn" ; (skip next instruction, clear Zf) ; 'BYTE' ; ====== ; "BYTE" BASIC command. ;; BYTE l0dffh: xor a ;0dff Set Zf push af ;0e00 Push flags on sack rst 8 ;0e01 Get address (call EVAL_INT_EXP) push hl ;0e02 Save address on stack. call l0005h ;0e03 Get content (call EVAL_INT_EXP_NEXT) ex (sp),hl ;0e06 HL = address pop bc ;0e07 BC = content ld (hl),c ;0e08 Load lower byte into (HL) pop af ;0e09 Get flags from stack jr z,l0e0eh ;0e0a If Zf not set, store upper byte into (HL+1) inc hl ;0e0c ld (hl),b ;0e0d l0e0eh: rst 30h ;0e0e f7 ; 'USR' ; ===== ; "USR" BASIC function. ;; USR l0e0fh: push de ;0e0f Save DE pointer on stack ld de,l0abdh ;0e10 Load INT_TO_FP_2 address on stack push de ;0e13 jp (hl) ;0e14 Jump to user function ; (returns back to INT_TO_FP_2, which pushes ; result to arithmetic stack and restores DE ; pointer) ;; CHR$ l0e15h: or d ;0e15 b2 ld a,l ;0e16 7d } ret ;0e17 c9 ;; FOR l0e18h: call l0974h ;0e18 cd 74 09 t call l0730h ;0e1b cd 30 07 0 ld (02aa1h),hl ;0e1e 22 a1 2a " * rst 18h ;0e21 df ld d,h ;0e22 54 T or d ;0e23 b2 rst 18h ;0e24 df ld c,a ;0e25 4f O xor a ;0e26 af rst 8 ;0e27 cf ld (02a6eh),hl ;0e28 22 6e 2a " n * ld l,0d8h ;0e2b 2e d8 . jp l0398h ;0e2d c3 98 03 ; 'SAVE' ; ====== ; "SAVE" BASIC command. ;; SAVE l0e30h: ld hl,02c36h ;0e32 Load BASIC_START address into HL... push hl ;0e33 ...and save it to stack ld hl,(02c38h) ;0e34 Load BASIC_END into HL rst 18h ;0e37 Call READ_PAR db 00dh ;0e38 ASCII CR db l0e3ch-$-1 ;0e39 jr l0e42h ;0e3a There is no parameter on the rest of the line. ; Jump forward ; This looks like the SAVE nnnn,nnnn variant of the command. l0e3ch: rst 8 ;0e3c Call EVAL_INT_EXP ex (sp),hl ;0e3d Replace BASIC_START address on the stack with ; the number from EVAL_INT_EXP call l0005h ;0e3e Call EVAL_INT_EXP_NEXT inc hl ;0e41 Replace BASIC_END address in HL with the number ; from EVAL_INT_EXP_NEXT + 1. l0e42h: pop de ;0e42 ...restore BASIC_START address into DE ; (address of the first byte of the block to ; save) ld b,060h ;0e43 Load 60h into B di ;0e45 Disable interrupts ; Save 96 sync bytes (00h) l0e46h: xor a ;0e46 Clear A call l0e68h ;0e47 Call SAVE_BYTE djnz l0e46h ;0e4a Repeat 96 times ; B = 0 and from now on holds the sum of all saved bytes. ; Save start byte (a5h) ld a,0a5h ;0e4c Load a5h into A call l0e68h ;0e4e Call SAVE_BYTE call l0e62h ;0e51 Call SAVE_WORD ; Saves DE (start address) call l0e62h ;0e54 Call SAVE_WORD ; Saves HL (end address) dec hl ;0e57 Decrement HL ; (address of the last byte of the block to save) ; Save data (from DE to HL inclusive) l0e58h: ld a,(de) ;0e58 Load byte at (DE) into A inc de ;0e59 Increment DE call l0e68h ;0e5a Call SAVE_BYTE jr nc,l0e58h ;0e5d If DE <= HL then loop ld a,b ;0e5f Load sum into A cpl ;0e60 Complement A (make checksum) ld e,a ;0e61 Load checksum into E ; Save checksum and one garbage byte in D and return. ; 'SAVE WORD' ; =========== ; This function saves one word (16 bits) to the audio cassette. ; Parameters: ; DE: word to be saved ; Returns: ; HL: holds previous contents of DE ; DE: holds previous contents of HL ; B: B + sum of both saved bytes ; Destroys: ; A ;; SAVE_WORD l0e62h: ex de,hl ;0e62 Exchange contents of DE and HL registers. ld a,l ;0e63 Load L into A call l0e68h ;0e64 Call SAVE_BYTE ld a,h ;0e67 Load H into A ; Save the second byte and return. ; 'SAVE BYTE' ; =========== ; This function saves one byte to the audio cassette. ; Parameters: ; A: byte to be saved ; Returns: ; B: B = B + A ; flags: output of CMP_HL_DE ; Destroys: ; A ;; SAVE_BYTE l0e68h: exx ;0e68 Exchange BC,DE,HL with BC',DE',HL' ld c,010h ;0e69 Load 16 into C ld hl,02038h ;0e6b Load latch address (2038h) into HL ;; SAVE_BYTE_LOOP l0e6eh: bit 0,c ;0e6e Test bit 0 of C jr z,l0e77h ;0e70 If C even, always make an impulse on output... rrca ;0e72 ...else rotate A right (stores LSB in C flag) ld b,064h ;0e73 load 100 into B jr nc,l0e86h ;0e75 skip impulse if LSB not set l0e77h: ld (hl),0fch ;0e77 Set output to high ld b,032h ;0e79 Load 50 into B l0e7bh: djnz l0e7bh ;0e7b Wait (approx. 650 T) ld (hl),0b8h ;0e7d Set output to low ld b,032h ;0e7f Load 50 into B l0e81h: djnz l0e81h ;0e81 Wait (approx. 650 T) ld (hl),0bch ;0e83 Set output to zero inc b ;0e85 Load 01h into B l0e86h: djnz l0e86h ;0e86 Wait (13 or 1300 T, depending whether ; C was even or odd) l0e88h: djnz l0e88h ;0e88 Wait (approx. 3328 T) dec c ;0e8a Decrement C jr nz,l0e6eh ;0e8b Jump to SAVE_BYTE_LOOP ; BC = 0 ; The following loop waits for 512 cycles l0e8dh: inc bc ;0e8d Increment BC bit 1,b ;0e8e Test bit 1 of B... jr z,l0e8dh ;0e90 ... and loop if not set. exx ;0e92 Exchange BC,DE,HL with BC',DE',HL' ; LOAD_BYTE function jumps here at the end l0e93h: add a,b ;0e93 Add B to A ld b,a ;0e94 B = A + B rst 10h ;0e95 Call CMP_HL_DE ret ;0e96 Return ; 'OLD' ; ===== ; "OLD" BASIC command. ; Verifies saved data if there is a '?' character after the command. ;; OLD l0e97h: rst 18h ;0e97 Call READ_PAR db '?' ;0e98 db l0e9ah-$-1 ;0e99 Check for '?' in the argument l0e9ah: push af ;0e9a Push result of READ_PAR. rst 18h ;0e9b Call READ_PAR db 00dh ;0e9c ASCII CR db l0ea0h-$-1 ;0e9d rst 28h ;0e9e Call CLEAR_HL db 03eh ;0e9f Dummy ld a, (ignores "rst 8" instruction, ; if there was no parameter) l0ea0h: rst 8 ;0ea0 Read load offset to HL (Call EVAL_INT_EXP) push hl ;0ea1 Store load offset on stack di ;0ea2 Disable interrupts ; Wait for the start byte (a5h) l0ea3h: call l0eddh ;0ea3 Call LOAD_BYTE ld a,c ;0ea6 cp 0a5h ;0ea7 Compare loaded byte with a5h and ... jr nz,l0ea3h ;0ea9 ... loop if not equal. ld b,a ;0eab Load a5h into B ; (value of the first loaded byte, for checksum) call l0ed9h ;0eac Call LOAD_WORD (start address) ld h,c ;0eaf pop de ;0eb0 Fetch load offset from stack and add it push de ;0eb1 to the start address. add hl,de ;0eb2 ex de,hl ;0eb3 DE = start address call l0ed9h ;0eb4 Call LOAD_WORD (end address) ld h,c ;0eb7 dec hl ;0eb8 Decrement end address ; HL = address of last byte to load ld a,b ;0eb9 Save checksum to A pop bc ;0eba Add load offset add hl,bc ;0ebb ld b,a ;0ebc Restore checksum to B l0ebdh: ex de,hl ;0ebd HL = address of the current byte ; DE = address of the last byte call l0eddh ;0ebe Call LOAD_BYTE ex af,af' ;0ec1 F' holds results of CMP_HL_DE ld a,c ;0ec2 Compare loaded byte with (HL) and ... cp (hl) ;0ec3 jr z,l0ecbh ;0ec4 ... jump forward if equal pop af ;0ec6 Restore flags jr z,l0ed6h ;0ec7 Jump to WHAT_RST if loaded byte not equal ; to memory byte and verify mode is enabled ; (Z flag is set by the READ_PAR function). push af ;0ec9 Save flags ld (hl),c ;0eca Store loaded byte in memory l0ecbh: inc hl ;0ecb Increment HL ex de,hl ;0ecc DE = address of the current byte ; HL = address of the last byte ex af,af' ;0ecd jr c,l0ebdh ;0ece Loop if current address < last address call l0eddh ;0ed0 Call LOAD_BYTE (checksum) pop af ;0ed3 Remove stored flags from stack inc b ;0ed4 Return if B = ffh, else display WHAT? error ret z ;0ed5 l0ed6h: jp l078fh ;0ed6 Jump to WHAT_RST ; Tape loading error ; 'LOAD WORD' ; =========== ; Loads one word (16 bits) from the audio cassette. It executes CMP_HL_DE ; function and returns its result in the flag register. ; Returns: ; L: low byte ; C: high byte ; B: B = B + sum of both loaded bytes ; flags: Result of the CMP_HL_DE function. ;; LOAD_WORD l0ed9h: call l0eddh ;0ed9 Call LOAD_BYTE ld l,c ;0edc Save result into L ; Continue with another LOAD_BYTE and return ; 'LOAD BYTE' ; =========== ; This function loads one byte from the audio cassette. ; Returns: ; C: byte loaded ; B: B = B + C ;; LOAD_BYTE l0eddh: exx ;0edd ld b,001h ;0ede Load 1 into B' l0ee0h: ld a,0a7h ;0ee0 Load 167 into A ; This block waits for an impulse on the cassette port. If B' = 1 (on first ; iteration), it waits indefinitely. If B' = 0 it waits for A loop iterations. ; 49 T * 167 = 8183 T = 2.7ms ;; LOAD_WAIT_PULSE l0ee2h: add a,b ;0ee2 4 Add B' to A ld hl,02000h ;0ee3 10 Load cassette port address into HL' bit 0,(hl) ;0ee6 12 Test bit 0 jr z,l0ef1h ;0ee8 12,7 If impulse was detected, break loop... dec a ;0eea 4 ...else decrement A and jr nz,l0ee2h ;0eeb 12,7 loop to LOAD_WAIT_PULSE if not zero exx ;0eed 4 ld a,c ;0eee 4 Load C into A jr l0e93h ;0eef B = A + B, compare HL with DE ; and return from function l0ef1h: ; Pause for 4369 T = 1.42 ms ld b,0dah ;0ef1 7 Load 218 into B' l0ef3h: ld a,0a9h ;0ef3 7 Load a9h into A djnz l0ef3h ;0ef5 13,8 Loop ld b,05ah ;0ef7 7 Load 90 into B' ; This loop checks if the first pulse is followed by another one in specified ; time interval. It has 90 iterations, each 35 T (11.4us) long. If input is ; low in more than 4 iterations (so that A doesn't overflow), then bit 1 is ; loaded into C. Else bit 0. ; This gives a minimum pulse width of 140 T (45 us). ;; LOAD_READ_PULSE l0ef9h: ld c,(hl) ;0ef9 7 Load C' from cassette port rr c ;0efa 8 Move bit 0 into carry flag... adc a,000h ;0efc 7 ...and add it to A djnz l0ef9h ;0efe 13,8 Loop to LOAD_READ_PULSE rlca ;0f00 Rotate A left (MSB into carry flag) exx ;0f01 rr c ;0f02 Rotate C right (carry flag into MSB) exx ;0f04 jr l0ee0h ;0f05 Loop LOAD_WAIT_PULSE (B' = 0) ; ********************************** ; * BASIC INTERPRETER DATA SECTION * ; ********************************** ; 'READY STRING' ; ============== ; Command prompt string, printed when in command line mode. ;; READY l0f07h: dm 040h, 027h ;0f07 dm "READY", 00dh ;0f09 ; 'BASIC TABLES' ; ============== ; Following section contains tables for the BASIC interpreter ; (NOTE: complete table must fit into 0fxxh page) ; Format of the table is: ; dm "function name" ; db "high byte of function's address" + 80h ; ( + 40h if function takes an integer argument in ; parenthesis) ; db "low byte of function's address" ; A catch-all rule lacks the function name. ; 'BASIC COMMAND LINE TABLE' ; ========================== ; These BASIC commands take zero or one integer argument and can only appear ; on the command line. ;; BASIC_CMDLINE_TABLE l0f0fh: dm "LIST" ;0f0f db ((l045eh>>8)&00ffh)+80h ;0f13 db (l045eh)&00ffh ;0f14 dm "RUN" ;0f15 db ((l040bh>>8)&00ffh)+80h ;0f18 db (l040bh)&00ffh ;0f19 dm "NEW" ;0f1a db ((l03fch>>8)&00ffh)+80h ;0f1d db (l03fch)&00ffh ;0f1e dm "SAVE" ;0f1f db ((l0e30h>>8)&00ffh)+80h ;0f23 db (l0e30h)&00ffh ;0f24 dm "OLD" ;0f25 db ((l0e97h>>8)&00ffh)+80h ;0f28 db (l0e97h)&00ffh ;0f29 dm "EDIT" ;0f2a db ((l0299h>>8)&00ffh)+80h ;0f2e db (l0299h)&00ffh ;0f2f ; 'BASIC COMMAND TABLE' ; ===================== ; These BASIC commands take zero or one integer argument and jump to ; RUN_THIS_LINE routine (or one of interpreter's other entry points) at the ; end. ;; BASIC_CMD_TABLE l0f30h: dm "NEXT" ;0f30 db ((l0564h>>8)&00ffh)+80h ;0f34 db (l0564h)&00ffh ;0f35 dm "INPUT" ;0f36 db ((l066ch>>8)&00ffh)+80h ;0f3b db (l066ch)&00ffh ;0f3c dm "IF" ;0f3d db ((l0441h>>8)&00ffh)+80h ;0f3f db (l0441h)&00ffh ;0f40 dm "GOTO" ;0f41 db ((l0453h>>8)&00ffh)+80h ;0f45 db (l0453h)&00ffh ;0f46 dm "CALL" ;0f47 db ((l04f4h>>8)&00ffh)+80h ;0f4b db (l04f4h)&00ffh ;0f4c dm "UNDOT" ;0f4d db ((l06cch>>8)&00ffh)+80h ;0f52 db (l06cch)&00ffh ;0f53 dm "RET" ;0f54 db ((l0512h>>8)&00ffh)+80h ;0f57 db (l0512h)&00ffh ;0f58 dm "TAKE" ;0f59 db ((l0626h>>8)&00ffh)+80h ;0f5d db (l0626h)&00ffh ;0f5e dm "!" ;0f5f db ((l044dh>>8)&00ffh)+80h ;0f60 db (l044dh)&00ffh ;0f61 dm "#" ;0f62 db ((l044dh>>8)&00ffh)+80h ;0f63 db (l044dh)&00ffh ;0f64 dm "FOR" ;0f65 db ((l0e18h>>8)&00ffh)+80h ;0f68 db (l0e18h)&00ffh ;0f69 dm "PRINT" ;0f69 db ((l0480h>>8)&00ffh)+80h ;0f6f db (l0480h)&00ffh ;0f70 dm "DOT" ;0f71 db ((l06cfh>>8)&00ffh)+80h ;0f74 db (l06cfh)&00ffh ;0f75 dm "ELSE" ;0f76 db ((l044dh>>8)&00ffh)+80h ;0f7a db (l044dh)&00ffh ;0f7b dm "BYTE" ;0f7c db ((l0dffh>>8)&00ffh)+80h ;0f80 db (l0dffh)&00ffh ;0f81 dm "WORD" ;0f82 db ((l0dfeh>>8)&00ffh)+80h ;0f86 db (l0dfeh)&00ffh ;0f87 dm "ARR$" ;0f88 db ((l010bh>>8)&00ffh)+80h+40h ;0f8c db (l010bh)&00ffh ;0f8d dm "STOP" ;0f8e db ((l0317h>>8)&00ffh)+80h ;0f92 db (l0317h)&00ffh ;0f93 dm "HOME" ;0f94 db ((l04d6h>>8)&00ffh)+80h ;0f98 db (l04d6h)&00ffh ;0f99 ; Catch-all rule for BASIC commands. db ((l075bh>>8)&00ffh)+80h ;0f9a db (l075bh)&00ffh ;0f9b ; 'BASIC FUNCTION TABLE' ; ====================== ; These BASIC functions take zero or one integer arguments and leave a ; floating point value on the arithmetic stack after they return. ;; BASIC_FUNC_TABLE l0f9ch: dm "RND" ;0f9c db ((l0c8fh>>8)&00ffh)+80h ;0f9f db (l0c8fh)&00ffh ;0fa0 dm "MEM" ;0fa1 db ((l0d9ah>>8)&00ffh)+80h ;0fa4 db (l0d9ah)&00ffh ;0fa5 dm "KEY" ;0fa6 db ((l0dabh>>8)&00ffh)+80h+40h ;0fa9 db (l0dabh)&00ffh ;0faa dm "BYTE" ;0fab db ((l0dbch>>8)&00ffh)+80h+40h ;0faf db (l0dbch)&00ffh ;0fb0 dm "WORD" ;0fb1 db ((l0da5h>>8)&00ffh)+80h+40h ;0fb5 db (l0da5h)&00ffh ;0fb6 dm "PTR" ;0fb7 db ((l0769h>>8)&00ffh)+80h ;0fba db (l0769h)&00ffh ;0fbb dm "VAL" ;0fbc db ((l0770h>>8)&00ffh)+80h+40h ;0fbf db (l0770h)&00ffh ;0fc0 dm "EQ" ;0fc1 db ((l0dc2h>>8)&00ffh)+80h ;0fc3 db (l0dc2h)&00ffh ;0fc4 dm "INT" ;0fc5 db ((l0abch>>8)&00ffh)+80h+40h ;0fc8 db (l0abch)&00ffh ;0fc9 dm "&" ;0fca db ((l0ddfh>>8)&00ffh)+80h ;0fcb db (l0ddfh)&00ffh ;0fcc dm "USR" ;0fcd db ((l0e0fh>>8)&00ffh)+80h+40h ;0fd0 db (l0e0fh)&00ffh ;0fd1 dm "DOT" ;0fd2 db ((l06dch>>8)&00ffh)+80h ;0fd5 db (l06dch)&00ffh ;0fd6 ; Catch-all rule for BASIC functions. db ((l0777h>>8)&00ffh)+80h ;0fd7 db (l0777h)&00ffh ;0fd8 dm "STEP" ;0fd9 db ((l0528h>>8)&00ffh)+80h ;0fdd db (l0528h)&00ffh ;0fde ; Catch-all rule db ((l052ah>>8)&00ffh)+80h ;0fdf db (l052ah)&00ffh ;0fe0 ; 'BASIC "PRINT" COMMAND TABLE' ; ============================= ; These BASIC commands can only appear after the "PRINT" command. They ; jump to RUN_THIS_LINE routine (or one of interpreter's other entry points) ; at the end. ;; BASIC_PRINT_TABLE l0fe1h: dm "AT" ;0fe1 db ((l04bch>>8)&00ffh)+80h ;0fe3 db (l04bch)&00ffh ;0fe4 dm "X$" ;0fe5 db ((l0498h>>8)&00ffh)+80h ;0fe7 db (l0498h)&00ffh ;0fe8 dm "Y$" ;0fe9 db ((l049bh>>8)&00ffh)+80h ;0feb db (l049bh)&00ffh ;0fec ; Catch-all rule db ((l048eh>>8)&00ffh)+80h ;0fed db (l048eh)&00ffh ;0fee dm "CHR$" ;0fef db ((l0e15h>>8)&00ffh)+80h+40h ;0ff3 db (l0e15h)&00ffh ;0ff4 ; Catch-all rule db ((l060bh>>8)&00ffh)+80h ;0ff5 db (l060bh)&00ffh ;0ff6 dm "ELSE" ;0ff7 db ((l041ah>>8)&00ffh)+80h ;0ffb db (l041ah)&00ffh ;0ffc ; Catch-all rule db ((l081bh>>8)&00ffh)+80h ;0ffd db (l081bh)&00ffh ;0ffe ; One (!) spare byte db 00h ;0fff end